mirror of
https://www.gitlink.org.cn/mayx/mayx.gitlink.net
synced 2026-06-01 07:03:32 +08:00
Update 4 files
- /_data/other_repo_list.csv - /_posts/2026-06-01-dedupe.md - /assets/js/pjax.js - /index.html
This commit is contained in:
@@ -18,7 +18,6 @@ https://repo2.serv00.com/git/pub/Mayx/mayx/
|
|||||||
https://git.pixie.town/mayx/mayx
|
https://git.pixie.town/mayx/mayx
|
||||||
https://codefloe.com/mayx/blog
|
https://codefloe.com/mayx/blog
|
||||||
https://git.minetest.land/Mayx/blog
|
https://git.minetest.land/Mayx/blog
|
||||||
http://47.120.60.153:10880/mayx/blog
|
|
||||||
http://1.6.141.109:3000/mayx/blog
|
http://1.6.141.109:3000/mayx/blog
|
||||||
http://52.28.156.42/mayx/blog
|
http://52.28.156.42/mayx/blog
|
||||||
https://code.dsconce.space/mayx/blog
|
https://code.dsconce.space/mayx/blog
|
||||||
@@ -47,7 +46,6 @@ http://142.171.47.170:3000/mayx/blog
|
|||||||
http://111.231.146.230:8418/mayx/blog
|
http://111.231.146.230:8418/mayx/blog
|
||||||
https://git.pwaapp.cc/mayx/blog
|
https://git.pwaapp.cc/mayx/blog
|
||||||
https://an360.top/mayx/blog
|
https://an360.top/mayx/blog
|
||||||
http://106.15.78.64:3000/mayx/blog
|
|
||||||
http://111.119.244.185:3000/mayx/blog
|
http://111.119.244.185:3000/mayx/blog
|
||||||
https://git.influxfin.com/mayx/blog
|
https://git.influxfin.com/mayx/blog
|
||||||
http://219.157.255.213:25311/mayx/blog
|
http://219.157.255.213:25311/mayx/blog
|
||||||
@@ -129,8 +127,6 @@ http://101.201.34.43:3000/mayx/blog
|
|||||||
https://git.gloje-rinchen-dorjee-rinpoche-buddhist-monastery.org/mayx/blog
|
https://git.gloje-rinchen-dorjee-rinpoche-buddhist-monastery.org/mayx/blog
|
||||||
http://101.35.227.2:3000/mayx/blog
|
http://101.35.227.2:3000/mayx/blog
|
||||||
http://175.126.123.163:3000/mayx/blog
|
http://175.126.123.163:3000/mayx/blog
|
||||||
http://git.uhfdemo.com/mayx/blog
|
|
||||||
https://git.jakubzabski.pl/mayx/blog
|
|
||||||
http://209.141.47.52:3000/mayx/blog
|
http://209.141.47.52:3000/mayx/blog
|
||||||
http://60.204.158.188:3000/mayx/blog
|
http://60.204.158.188:3000/mayx/blog
|
||||||
http://60.204.156.211:3000/mayx/blog
|
http://60.204.156.211:3000/mayx/blog
|
||||||
@@ -198,7 +194,6 @@ http://docker.clhero.fun:3000/mayx/blog
|
|||||||
https://bdgit.educoder.net/mayx/blog
|
https://bdgit.educoder.net/mayx/blog
|
||||||
http://e19510c831.iok.la/mayx/blog
|
http://e19510c831.iok.la/mayx/blog
|
||||||
http://119.45.49.212:3000/mayx/blog
|
http://119.45.49.212:3000/mayx/blog
|
||||||
https://gitea.kdlsvps.top/mayx/blog
|
|
||||||
https://code.antopie.org/mayx/blog
|
https://code.antopie.org/mayx/blog
|
||||||
https://git.serenetia.com/mayx/blog
|
https://git.serenetia.com/mayx/blog
|
||||||
https://vcs.cozydsp.space/mayx/blog
|
https://vcs.cozydsp.space/mayx/blog
|
||||||
@@ -268,7 +263,6 @@ http://194.5.152.156:3000/mayx/blog
|
|||||||
http://8.155.172.147:3001/mayx/blog
|
http://8.155.172.147:3001/mayx/blog
|
||||||
https://git.erfmann.dev/mayx/blog
|
https://git.erfmann.dev/mayx/blog
|
||||||
https://git.weavefun.com:5443/mayx/blog
|
https://git.weavefun.com:5443/mayx/blog
|
||||||
https://git.vajdak.cz/mayx/blog
|
|
||||||
http://58.65.162.118:3000/mayx/blog
|
http://58.65.162.118:3000/mayx/blog
|
||||||
https://git.arkon.solutions/mayx/blog
|
https://git.arkon.solutions/mayx/blog
|
||||||
http://8.131.93.145:54082/mayx/blog
|
http://8.131.93.145:54082/mayx/blog
|
||||||
@@ -276,7 +270,6 @@ http://111.9.31.174:10007/mayx/blog
|
|||||||
https://forgejo.bridgetownrb.com/mayx/blog
|
https://forgejo.bridgetownrb.com/mayx/blog
|
||||||
http://54.199.96.217:3000/mayx/blog
|
http://54.199.96.217:3000/mayx/blog
|
||||||
http://20.219.0.85:3000/mayx/blog
|
http://20.219.0.85:3000/mayx/blog
|
||||||
https://dev01.open-alt.com/mayx/blog
|
|
||||||
https://gitea.doinlab.com/mayx/blog
|
https://gitea.doinlab.com/mayx/blog
|
||||||
https://git.7af.ru/mayx/blog
|
https://git.7af.ru/mayx/blog
|
||||||
https://gitea.yimoyuyan.cn/mayx/blog
|
https://gitea.yimoyuyan.cn/mayx/blog
|
||||||
@@ -289,14 +282,12 @@ https://dev.kiramtech.com/mayx/blog
|
|||||||
https://git.ihatemen.uk/mayx/blog
|
https://git.ihatemen.uk/mayx/blog
|
||||||
https://git.123doit.com/mayx/blog
|
https://git.123doit.com/mayx/blog
|
||||||
http://62.43.207.91:8889/mayx/blog
|
http://62.43.207.91:8889/mayx/blog
|
||||||
https://rsas.de/mayx/blog
|
|
||||||
https://git.johntsai.online/mayx/blog
|
https://git.johntsai.online/mayx/blog
|
||||||
https://gitea.css-sistemas.com.br/mayx/blog
|
https://gitea.css-sistemas.com.br/mayx/blog
|
||||||
https://git.f4e.lol/mayx/blog
|
https://git.f4e.lol/mayx/blog
|
||||||
http://47.113.145.232:3000/mayx/blog
|
http://47.113.145.232:3000/mayx/blog
|
||||||
http://47.115.212.237:3000/mayx/blog
|
http://47.115.212.237:3000/mayx/blog
|
||||||
http://72.61.229.93:4000/mayx/blog
|
http://72.61.229.93:4000/mayx/blog
|
||||||
https://git.yinbonet.cn/mayx/blog
|
|
||||||
https://lishan148.synology.me:3014/mayx/blog_cn
|
https://lishan148.synology.me:3014/mayx/blog_cn
|
||||||
http://1.95.221.174:3000/mayx/blog
|
http://1.95.221.174:3000/mayx/blog
|
||||||
https://git.huwhy.cn/mayx/blog_cn
|
https://git.huwhy.cn/mayx/blog_cn
|
||||||
@@ -345,8 +336,6 @@ https://git.gnyra.com/mayx/blog
|
|||||||
https://git.graveyard.sh/mayx/blog
|
https://git.graveyard.sh/mayx/blog
|
||||||
https://git.nathanspackman.com/mayx/blog
|
https://git.nathanspackman.com/mayx/blog
|
||||||
https://git.rmarl.in/mayx/blog
|
https://git.rmarl.in/mayx/blog
|
||||||
http://git.qniao.cn/mayx/blog
|
|
||||||
https://git.fast-blast.uk/mayx/blog
|
|
||||||
http://git.mynas71.myds.me/mayx/blog
|
http://git.mynas71.myds.me/mayx/blog
|
||||||
https://git.4lsa.com/mayx/blog
|
https://git.4lsa.com/mayx/blog
|
||||||
https://rlgit.pro/mayx/blog
|
https://rlgit.pro/mayx/blog
|
||||||
@@ -367,7 +356,6 @@ https://git.webtims.ru/mayx/blog
|
|||||||
https://gitea.personalsoftware.space/mayx/blog
|
https://gitea.personalsoftware.space/mayx/blog
|
||||||
http://gitea.yiban.com.tw:3030/mayx/blog
|
http://gitea.yiban.com.tw:3030/mayx/blog
|
||||||
https://gitlab.iplusus.com/mayx/blog
|
https://gitlab.iplusus.com/mayx/blog
|
||||||
https://git.cyberuk.me/mayx/blog
|
|
||||||
https://gitea.zachl.tech/mayx/blog
|
https://gitea.zachl.tech/mayx/blog
|
||||||
https://git.miasma-os.com/mayx/blog
|
https://git.miasma-os.com/mayx/blog
|
||||||
https://gitea.nacsity.cn/mayx/blog
|
https://gitea.nacsity.cn/mayx/blog
|
||||||
@@ -383,12 +371,10 @@ http://47.105.124.101:3000/mayx/blog_cn
|
|||||||
http://49.232.183.190:3000/mayx/blog_cn
|
http://49.232.183.190:3000/mayx/blog_cn
|
||||||
http://git.zxkedu.com:33769/mayx/blog_cn
|
http://git.zxkedu.com:33769/mayx/blog_cn
|
||||||
https://code.wemediacn.com/mayx/blog_cn
|
https://code.wemediacn.com/mayx/blog_cn
|
||||||
https://gitea.shizuka.icu/mayx/blog_cn
|
|
||||||
http://51.159.198.233:3000/mayx/blog
|
http://51.159.198.233:3000/mayx/blog
|
||||||
https://gitea.adriangonzalezbarbosa.eu/mayx/blog
|
https://gitea.adriangonzalezbarbosa.eu/mayx/blog
|
||||||
https://git.legatus.ru/mayx/blog
|
https://git.legatus.ru/mayx/blog
|
||||||
https://git.kayashov.keenetic.pro/mayx/blog
|
https://git.kayashov.keenetic.pro/mayx/blog
|
||||||
http://43.138.83.20:3000/mayx/blog_cn
|
|
||||||
http://47.104.241.192:19999/mayx/blog_cn
|
http://47.104.241.192:19999/mayx/blog_cn
|
||||||
http://47.98.148.146:1026/mayx/blog_cn
|
http://47.98.148.146:1026/mayx/blog_cn
|
||||||
http://119.96.62.56:3000/mayx/blog_cn
|
http://119.96.62.56:3000/mayx/blog_cn
|
||||||
@@ -447,7 +433,6 @@ https://silica.codes/mayx/blog
|
|||||||
https://git.crystalyx.net/mayx/blog
|
https://git.crystalyx.net/mayx/blog
|
||||||
https://gittea.dev/mayx/blog
|
https://gittea.dev/mayx/blog
|
||||||
https://git.newnaturalphilosophy.org/mayx/blog
|
https://git.newnaturalphilosophy.org/mayx/blog
|
||||||
http://mrjinit.com:3000/mayx/blog
|
|
||||||
https://code.infininov.com/mayx/blog
|
https://code.infininov.com/mayx/blog
|
||||||
https://git.apt142.ru/mayx/blog
|
https://git.apt142.ru/mayx/blog
|
||||||
http://gitea.mcelwain.net/mayx/blog
|
http://gitea.mcelwain.net/mayx/blog
|
||||||
@@ -476,7 +461,6 @@ http://zzdgitea.stnav.com/mayx/blog_cn
|
|||||||
http://1.117.66.197:3000/mayx/blog_cn
|
http://1.117.66.197:3000/mayx/blog_cn
|
||||||
http://git.zhmight.com/mayx/blog_cn
|
http://git.zhmight.com/mayx/blog_cn
|
||||||
https://intl-dev.gaia888.com/mayx/blog_cn
|
https://intl-dev.gaia888.com/mayx/blog_cn
|
||||||
https://gitea.xinyuxi.com/mayx/blog_cn
|
|
||||||
http://gitea.snailtrack.cn/mayx/blog_cn
|
http://gitea.snailtrack.cn/mayx/blog_cn
|
||||||
http://ydds.cloud:3000/mayx/blog_cn
|
http://ydds.cloud:3000/mayx/blog_cn
|
||||||
http://120.24.50.145:3000/mayx/blog_cn
|
http://120.24.50.145:3000/mayx/blog_cn
|
||||||
@@ -488,7 +472,6 @@ http://www.arkproject.top/mayx/blog
|
|||||||
http://www.bkandssp.cn:30/mayx/blog
|
http://www.bkandssp.cn:30/mayx/blog
|
||||||
https://gitea.spitaki.cloud/mayx/blog
|
https://gitea.spitaki.cloud/mayx/blog
|
||||||
https://git.codle.ru/mayx/blog
|
https://git.codle.ru/mayx/blog
|
||||||
https://git.sao.ru/mayx/blog
|
|
||||||
https://codeop.ru/mayx/blog
|
https://codeop.ru/mayx/blog
|
||||||
https://git.mirocom.org/mayx/blog
|
https://git.mirocom.org/mayx/blog
|
||||||
http://gitea.ydxtool.com/mayx/blog
|
http://gitea.ydxtool.com/mayx/blog
|
||||||
@@ -502,7 +485,6 @@ https://git.kraevsky.ru/mayx/blog
|
|||||||
https://ruyiscx.cloud:3000/mayx/blog
|
https://ruyiscx.cloud:3000/mayx/blog
|
||||||
https://git.0xee.eu/mayx/blog
|
https://git.0xee.eu/mayx/blog
|
||||||
https://gitea.deitglobal.com/mayx/blog
|
https://gitea.deitglobal.com/mayx/blog
|
||||||
https://www.mygitea.ru/mayx/blog
|
|
||||||
https://git.crwlr.ir/mayx/blog
|
https://git.crwlr.ir/mayx/blog
|
||||||
https://git.nozora.top/mayx/blog
|
https://git.nozora.top/mayx/blog
|
||||||
https://git.sortug.com/mayx/blog
|
https://git.sortug.com/mayx/blog
|
||||||
@@ -525,6 +507,16 @@ http://namonba.asuscomm.com:3001/mayx/blog
|
|||||||
http://109.199.98.226:3001/mayx/blog
|
http://109.199.98.226:3001/mayx/blog
|
||||||
https://git.extra.eiffel.com/mayx/blog
|
https://git.extra.eiffel.com/mayx/blog
|
||||||
https://gitea.digitanie.org/mayx/blog
|
https://gitea.digitanie.org/mayx/blog
|
||||||
http://124.207.0.162:30000/mayx/blog
|
|
||||||
https://git.xleed.com/mayx/blog
|
https://git.xleed.com/mayx/blog
|
||||||
https://qlcodegitserver.online/mayx/blog
|
https://qlcodegitserver.online/mayx/blog
|
||||||
|
https://gitea.vvzvlad.xyz/mayx/blog
|
||||||
|
https://git.supernets.org/mayx/blog
|
||||||
|
https://git.digitaltelepresence.com/mayx/blog
|
||||||
|
https://git.hrfee.pw/mayx/blog
|
||||||
|
https://git.libregaming.org/mayx/blog
|
||||||
|
https://git.kaki87.net/mayx/blog
|
||||||
|
https://forgejo.vanten-s.com/mayx/blog
|
||||||
|
https://git.heartnn.com/mayx/blog
|
||||||
|
https://git.joinplu.me/mayx/blog
|
||||||
|
https://git.research.dezeeuw.ca/mayx/blog
|
||||||
|
http://149.104.29.239:8081/mayx/blog
|
||||||
|
|||||||
|
210
_posts/2026-06-01-dedupe.md
Normal file
210
_posts/2026-06-01-dedupe.md
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
---
|
||||||
|
layout: post
|
||||||
|
title: 如何节约游戏占用的硬盘空间?
|
||||||
|
tags: [dedupe, RPG制作大师, 游戏]
|
||||||
|
---
|
||||||
|
|
||||||
|
浪费硬盘空间是可耻的!<!--more-->
|
||||||
|
|
||||||
|
# 起因
|
||||||
|
在几年前,我写过一篇在[MacBook上玩游戏](/2023/10/21/game.html)的文章,在那之后,我已经在我的Mac上下载了几十部游戏。只不过有个问题……我的Mac只有256GiB的硬盘存储空间,下载一堆游戏会让我的硬盘空间不够用,但是又不太想删,所以我该怎么尽可能让游戏占用更少的空间呢?
|
||||||
|
首先为了能在Mac上尽可能流畅地玩,我玩的游戏大多都是用跨平台能力很强的引擎编写的游戏,比如[Ren'Py](https://github.com/renpy/renpy)、RPG制作大师、Godot之类的,而像RPG制作大师这种引擎制作的游戏还有一个特点,开发者一般都会使用引擎自带的素材进行开发,有时候还会用不少第三方的罐头素材之类的(实际上甚至还有好多AVG为了蹭这些引擎的公用素材刻意用它们),所以这几十个游戏里应该有非常多的重复素材,如果能想办法把它们去个重,应该能节省相当多的空间吧……
|
||||||
|
|
||||||
|
# 去重的方法
|
||||||
|
如果想要对文件进行去重,我搜了一下,有个叫做[jdupes](https://codeberg.org/jbruchon/jdupes)的工具就很不错,它支持多种去重方式,比如使用硬链接,或者用一些文件系统的写时复制特性。不过如果用写时复制特性,jdupes在第二次执行的时候会认为去重后的文件还是单独的文件,就会重复去重了,而且最终也不好统计,反正对我玩的游戏来说,要去重的都是游戏素材,不存在后续修改的可能性,所以我打算全部用硬链接。
|
||||||
|
所以最终要执行的命令也非常简单,直接一句`jdupes -r -L Game`就可以了,这样以后每次下载了新的游戏之后重复执行这个操作,就可以将游戏中和其他游戏里有的素材去重了。
|
||||||
|
不过实际上很多游戏并不能直接用这种方式去重,因为它们的资源文件有些是打包成单个文件,有些进行了简单的加密,导致即使是相同的素材,文件也并不相同,所以我必须让所有的资源以单独原始的形态出现。对于不同的引擎也有不同的处理方式,所以接下来我需要对它们进行一些研究。
|
||||||
|
|
||||||
|
# 不同引擎的处理方式
|
||||||
|
## RPG制作大师MV/MZ
|
||||||
|
对于RPG制作大师MV/MZ开发的游戏来说,解密很简单,比较知名的是一个叫做[RPG-Maker-MV-Decrypter](https://gitlab.com/Petschko/RPG-Maker-MV-Decrypter)的工具,它可以在浏览器中进行解密,但一个游戏的资源文件非常多……要是全上传给浏览器实在是太麻烦了……后来我又搜了一下,有一个用C#写的叫[RPG Maker Decrypter](https://github.com/uuksu/RPGMakerDecrypter)工具也很不错,它作为命令行工具比在浏览器中执行简单多了,而且还能只把资源文件单独提出来,这样就可以剔除掉游戏自带的浏览器文件。不过他这个仓库的代码有个问题,它在选择文件的时候似乎会区分大小写,文件夹名中含有大写字母的似乎会被剔除……这样不太符合我的要求啊,当然我不会C#,于是我用AI改了一下,还给他提了个[PR](https://github.com/uuksu/RPGMakerDecrypter/pull/28),不过这家伙看起来似乎不太喜欢AI写的代码,看起来不打算合我的PR😅。不过无所谓了,反正我也是自用,他爱合不合吧。
|
||||||
|
这个工具的用法也非常简单,一句`RPGMakerDecrypter-cli [input] -p -o [output]`就处理好了,处理完之后只需要把`data/System.json`中的`hasEncryptedImages`和`hasEncryptedAudio`设置为false就可以正常识别,以后在Mac中只要在游戏路径下执行`python3 -m http.server`就可以在浏览器中游玩了。
|
||||||
|
在这个过程中,我还发现有一些游戏喜欢把原画文件直接放到游戏里面,一张图片好几M,但RPG制作大师的引擎在渲染的时候根本不会渲染出那么高的分辨率,结果毫无意义地浪费一大堆存储空间,而且因为图片是加密的,对大多数人来说也没有收藏价值。所以在解密完之后我就想干脆把这些图片全部有损压缩一遍,估计能节省不少存储空间,于是让AI写了个简单的压缩脚本处理了一下:
|
||||||
|
```python
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
图片压缩脚本(多进程版本)
|
||||||
|
将 pictures.orig 文件夹中的图片使用 WebP 格式进行高效压缩,
|
||||||
|
保持分辨率不变,肉眼看不出差异,压缩后的图片保存到 pictures 文件夹。
|
||||||
|
|
||||||
|
使用方法:
|
||||||
|
python3 compress_images.py
|
||||||
|
|
||||||
|
压缩策略:
|
||||||
|
- 保持原始分辨率不变
|
||||||
|
- 使用 WebP 格式(有损压缩,高质量)
|
||||||
|
- 质量设置为 85,在保持视觉质量的同时显著减小文件大小
|
||||||
|
- 文件名和后缀保持不变
|
||||||
|
- 多进程并行处理
|
||||||
|
- 处理失败时自动复制原文件
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
from PIL import Image
|
||||||
|
from pathlib import Path
|
||||||
|
from multiprocessing import Pool, cpu_count
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
# 配置路径
|
||||||
|
SOURCE_DIR = "pictures.orig"
|
||||||
|
OUTPUT_DIR = "pictures"
|
||||||
|
|
||||||
|
# WebP 质量设置 (0-100,数值越高质量越好,文件也越大)
|
||||||
|
# 85 是一个很好的平衡点,肉眼几乎看不出差异
|
||||||
|
WEBP_QUALITY = 85
|
||||||
|
|
||||||
|
# 对于带有透明通道的图片,可以设置不同的质量
|
||||||
|
WEBP_QUALITY_WITH_ALPHA = 80
|
||||||
|
|
||||||
|
# 并行进程数,默认为 CPU 核心数
|
||||||
|
NUM_WORKERS = cpu_count()
|
||||||
|
|
||||||
|
|
||||||
|
def compress_single_image(img_file: tuple[str, str, str]) -> tuple[str, bool, int, int]:
|
||||||
|
"""
|
||||||
|
压缩单个图片文件(用于多进程)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
img_file: (源文件路径, 输出文件路径, 输出目录) 元组
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(文件名, 是否成功, 原始大小, 压缩后大小) 元组
|
||||||
|
"""
|
||||||
|
source_path, output_path_str, output_dir = img_file
|
||||||
|
source_path = Path(source_path)
|
||||||
|
output_path = Path(output_path_str)
|
||||||
|
|
||||||
|
original_size = source_path.stat().st_size
|
||||||
|
|
||||||
|
try:
|
||||||
|
img = Image.open(source_path)
|
||||||
|
|
||||||
|
# 检查是否有透明通道
|
||||||
|
has_alpha = img.mode in ('RGBA', 'LA', 'PA') or (img.mode == 'P' and 'transparency' in img.info)
|
||||||
|
|
||||||
|
# 确定使用的质量
|
||||||
|
quality = WEBP_QUALITY_WITH_ALPHA if has_alpha else WEBP_QUALITY
|
||||||
|
|
||||||
|
# 保存为 WebP 格式,但使用原始的文件扩展名
|
||||||
|
img.save(
|
||||||
|
str(output_path),
|
||||||
|
format='WEBP',
|
||||||
|
quality=quality,
|
||||||
|
method=6 # 压缩方法 0-6,6 是最慢但压缩率最高的
|
||||||
|
)
|
||||||
|
|
||||||
|
compressed_size = output_path.stat().st_size
|
||||||
|
return (source_path.name, True, original_size, compressed_size)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# 处理失败时,复制原文件到输出目录
|
||||||
|
try:
|
||||||
|
shutil.copy2(source_path, output_path)
|
||||||
|
compressed_size = output_path.stat().st_size
|
||||||
|
return (source_path.name, False, original_size, compressed_size)
|
||||||
|
except Exception as copy_error:
|
||||||
|
return (source_path.name, False, original_size, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
source_dir = Path(SOURCE_DIR)
|
||||||
|
output_dir = Path(OUTPUT_DIR)
|
||||||
|
|
||||||
|
# 检查源目录是否存在
|
||||||
|
if not source_dir.exists():
|
||||||
|
print(f"错误: 源目录 '{SOURCE_DIR}' 不存在")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 创建输出目录
|
||||||
|
output_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
# 获取所有图片文件(支持多种格式)
|
||||||
|
image_extensions = ('*.png', '*.jpg', '*.jpeg', '*.bmp', '*.gif', '*.tiff', '*.webp')
|
||||||
|
image_files = []
|
||||||
|
for ext in image_extensions:
|
||||||
|
image_files.extend(source_dir.glob(ext))
|
||||||
|
image_files = sorted(set(image_files)) # 去重并排序
|
||||||
|
|
||||||
|
if not image_files:
|
||||||
|
print(f"在 '{SOURCE_DIR}' 中没有找到图片文件")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 构建任务列表
|
||||||
|
tasks = []
|
||||||
|
for img_file in image_files:
|
||||||
|
output_path = output_dir / img_file.name # 保持原文件名和后缀
|
||||||
|
tasks.append((str(img_file), str(output_path), str(output_dir)))
|
||||||
|
|
||||||
|
print(f"找到 {len(tasks)} 个图片文件")
|
||||||
|
print(f"源目录: {SOURCE_DIR}")
|
||||||
|
print(f"输出目录: {OUTPUT_DIR}")
|
||||||
|
print(f"WebP 质量设置: {WEBP_QUALITY}")
|
||||||
|
print(f"并行进程数: {NUM_WORKERS}")
|
||||||
|
print("-" * 70)
|
||||||
|
|
||||||
|
# 使用多进程池处理图片
|
||||||
|
success_count = 0
|
||||||
|
fail_count = 0
|
||||||
|
total_original = 0
|
||||||
|
total_compressed = 0
|
||||||
|
|
||||||
|
with Pool(processes=NUM_WORKERS) as pool:
|
||||||
|
for i, (filename, success, original_size, compressed_size) in enumerate(pool.imap(compress_single_image, tasks), 1):
|
||||||
|
total_original += original_size
|
||||||
|
total_compressed += compressed_size
|
||||||
|
|
||||||
|
if success:
|
||||||
|
success_count += 1
|
||||||
|
marker = "✓"
|
||||||
|
reduction = (1 - compressed_size / original_size) * 100 if original_size > 0 else 0
|
||||||
|
status_msg = f"{reduction:+.1f}%"
|
||||||
|
else:
|
||||||
|
fail_count += 1
|
||||||
|
marker = "✗"
|
||||||
|
status_msg = "复制原文件"
|
||||||
|
|
||||||
|
status = f"[{i}/{len(tasks)}] {filename}"
|
||||||
|
print(f"{marker} {status:50} {original_size/1024:>8.1f}KB -> {compressed_size/1024:>8.1f}KB ({status_msg})")
|
||||||
|
|
||||||
|
# 输出总结
|
||||||
|
print("-" * 70)
|
||||||
|
total_reduction = (1 - total_compressed / total_original) * 100 if total_original > 0 else 0
|
||||||
|
print(f"压缩完成!")
|
||||||
|
print(f" 成功处理: {success_count}/{len(tasks)} 个文件")
|
||||||
|
if fail_count > 0:
|
||||||
|
print(f" 失败(已复制原文件): {fail_count}/{len(tasks)} 个文件")
|
||||||
|
print(f" 原始总大小: {total_original / 1024 / 1024:.2f} MB ({total_original / 1024:.1f} KB)")
|
||||||
|
print(f" 压缩后大小: {total_compressed / 1024 / 1024:.2f} MB ({total_compressed / 1024:.1f} KB)")
|
||||||
|
print(f" 总压缩率: {total_reduction:.1f}%")
|
||||||
|
print(f" 节省空间: {(total_original - total_compressed) / 1024 / 1024:.2f} MB")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
最终压缩完之后我把原图上传到了[EH画廊](https://e-hentai.org/g/3901673/426a7a17ba/)中,本地只留压缩后的图片,大小从原来的2GiB多下降到了300多MiB,可以说效果相当显著了。
|
||||||
|
除此之外还有一些游戏使用了Ogg FLAC背景音乐,这种音乐不仅占用磁盘空间很大,而且我在Safari上玩的时候浏览器根本没法解析(Chrome应该可以)。虽然我听音乐是会考虑[HiFi](/2025/03/22/hifi.html),但玩游戏就没必要了吧……所以像这种音乐,就得用一句:
|
||||||
|
```bash
|
||||||
|
ffmpeg -i input.flac.ogg -c:a vorbis -strict -2 -q:a 10 output.ogg
|
||||||
|
```
|
||||||
|
转换为正常有损的Ogg音乐了。
|
||||||
|
## RPG制作大师XP/VX/VA
|
||||||
|
对于RPG制作大师XP/VX/VA引擎开发的游戏来说,它们都是基于用Ruby语言开发的RGSS编写的,作为脚本来说,倒是有跨平台的条件,但因为官方并没有做跨平台,所以不能直接在Mac上运行。不过有一款叫做[mkxp-z](https://github.com/mkxp-z/mkxp-z)的工具允许跨平台运行使用RPG制作大师XP/VX/VA制作的游戏,因此这类游戏我也收集了一些。
|
||||||
|
这些游戏的资源通常会进行简单的混淆加密,一般会打包成单个RGSSAD文件,这个解包也很简单,用刚刚的RPG Maker Decrypter就可以。不过这种游戏还有个特点,有些游戏需要使用[RTP](https://www.rpgmakerweb.com/run-time-package)才能运行,它这个RTP其实就是RPG制作大师自带的素材包,当时设计出来估计也是想着用来节约硬盘空间吧,就是不知道为什么到后来的MV/MZ却取消了这种方式……虽然mkxp-z是支持通过配置文件引入RTP的,但既然我已经选择了硬链接的方式,就没必要单独搞RTP了,我选择把RTP直接和游戏合并,然后让jdupes直接去重就好了,这样相比于RTP的方式还有一些好处就是XP/VX/VA可能有一些和MV/MZ使用相同的素材,这部分也可以不用占用重复的空间了。
|
||||||
|
## Ren'Py
|
||||||
|
对于Ren'Py来说,因为这个引擎并没有自带的公共资源,所以重复素材的问题并不是很大。不过在我之前对[Ren'Py的探索](/2024/01/20/renpy.html)中提到过,我玩的一些游戏是系列游戏,这种系列游戏有非常多的素材复用,但显然开发者并不会为了节约玩家硬盘空间而共享这部分资源,而且Ren'Py游戏也都是打包成单个文件的,所以接下来我们依然得要解包才能进行去重处理。
|
||||||
|
Ren'Py使用的rpa文件解包起来依然很简单,有一款现成的工具[unrpa](https://github.com/Lattyware/unrpa)可以直接解包,用pip就能安装。不知道为什么这些引擎总是喜欢把资源文件都打成一个包,明明很容易就能解包……难道是为了性能吗?
|
||||||
|
不过也正是因为Ren'Py的公共资源不多,如果玩的不是系列游戏,就没有解包的必要了,解包之后一堆小文件有可能会比整个rpa文件更大,毕竟文件系统存在“簇”,有可能会消耗没对齐的空间。
|
||||||
|
|
||||||
|
# 验证结果
|
||||||
|
最终进行完上述操作,可以通过执行`du -sh`和`du -shl`进行对比来验证节约的硬盘空间,我在这次游戏的瘦身中节约了:
|
||||||
|
```
|
||||||
|
~ % du -sh Game
|
||||||
|
33G Game
|
||||||
|
~ % du -shl Game
|
||||||
|
47G Game
|
||||||
|
```
|
||||||
|
看起来还是相当可观啊……尤其是在当下硬盘价格大涨的情况下,如果很多人能通过这些方式来节约硬盘空间,就能减少对硬盘容量的需求吧……不过说到底其实也都是网上能下到的资源,也许玩完之后就删掉才是最好的节约硬盘的方式吧😂。
|
||||||
|
|
||||||
|
<input name="live2dBGM" value="https://music.163.com/song/media/outer/url?id=1968116350.mp3" type="hidden" />
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
|
|
||||||
/** 暴露给模板内 onclick/onchange 调用的导航函数 */
|
/** 暴露给模板内 onclick/onchange 调用的导航函数 */
|
||||||
window.go = function (url) {
|
window.go = function (url) {
|
||||||
$.pjax({ url: url, ...PJAX_OPTS });
|
$.pjax($.extend({ url: url }, PJAX_OPTS));
|
||||||
};
|
};
|
||||||
|
|
||||||
// ========== 初始化 ==========
|
// ========== 初始化 ==========
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ image: https://screenshot.mayx.eu.org/
|
|||||||
<!-- 遍历分页后的文章 -->
|
<!-- 遍历分页后的文章 -->
|
||||||
<table class="entry-content h-feed">
|
<table class="entry-content h-feed">
|
||||||
{% for post in paginator.posts %}
|
{% for post in paginator.posts %}
|
||||||
<tr><td class="h-entry" onclick="if (!event.target.closest('a.p-category')) go('{{ post.url }}')">
|
<tr><td class="h-entry" data-url="{{ post.url }}">
|
||||||
<h2 class="p-name"><a class="post-link u-url" href="{{ post.url }}">{{ post.title }}{% if post.layout == "encrypt" %} [加密] {% endif %}</a></h2>
|
<h2 class="p-name"><a class="post-link u-url" href="{{ post.url }}">{{ post.title }}{% if post.layout == "encrypt" %} [加密] {% endif %}</a></h2>
|
||||||
<p>
|
<p>
|
||||||
<time class="date dt-published" datetime="{{ post.date | date_to_xmlschema }}">{{ post.date | date: "%-d %B %Y" }}</time>
|
<time class="date dt-published" datetime="{{ post.date | date_to_xmlschema }}">{{ post.date | date: "%-d %B %Y" }}</time>
|
||||||
@@ -78,3 +78,10 @@ image: https://screenshot.mayx.eu.org/
|
|||||||
An <a href="https://xn--sr8hvo.ws">IndieWeb Webring</a> 🕸💍
|
An <a href="https://xn--sr8hvo.ws">IndieWeb Webring</a> 🕸💍
|
||||||
<a href="https://xn--sr8hvo.ws/next">→</a><br /><a href="https://icp.gov.moe/?keyword=20218888" target="_blank">萌ICP备 20218888号</a></small>
|
<a href="https://xn--sr8hvo.ws/next">→</a><br /><a href="https://icp.gov.moe/?keyword=20218888" target="_blank">萌ICP备 20218888号</a></small>
|
||||||
<input name="live2dBGM" value="https://music.163.com/song/media/outer/url?id=523658881.mp3" type="hidden" />
|
<input name="live2dBGM" value="https://music.163.com/song/media/outer/url?id=523658881.mp3" type="hidden" />
|
||||||
|
<script>
|
||||||
|
$(document).on('click', '.h-entry', function(e) {
|
||||||
|
if ($(e.target).closest('.p-category').length === 0) {
|
||||||
|
go($(this).attr('data-url'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user