<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[如何正确找到解包游戏的工具]]></title><description><![CDATA[<h2><a class="anchor-offset" name="背景"></a>背景</h2>
<p dir="auto">近几天，我成功解包了游戏《ご注文はうさぎですか?? Wonderful Party!》（PSV），在KUN的建议下，我决定记录下这次解包的经历，（<s>顺带本文参考了<a href="https://soft.moe/professionalism/kun-source.html" target="_blank" rel="noopener noreferrer nofollow ugc">在遇到自己完全不会的技术栈时应该如何做?</a>的结构编写</s>）</p>
<h2><a class="anchor-offset" name="必要性"></a>必要性</h2>
<p dir="auto">实际上，我们解包大多数依赖于别人写的工具（当然如果没有的话有可能你得自己写一个，这很麻烦...），但是大部分情况通过网络上的工具就可以搞定，所以如何快速寻找这些工具就显得很重要了</p>
<h2><a class="anchor-offset" name="方法论"></a>方法论</h2>
<p dir="auto">以<a href="https://nyaa.si/view/855174" target="_blank" rel="noopener noreferrer nofollow ugc">《ご注文はうさぎですか?? Wonderful Party!》（PSV）</a>为例，首先我们发现，这是一个单文件，文件名为<code>PCSG00786_FULLGAME_01.00_C9_V2.0.vpk</code>，这是一个未知扩展名的文件，所以我们首先应当用16进制编辑器查看</p>
<p dir="auto"><img src="/assets/uploads/files/1731722373800-1c5c680e-6b45-4438-a078-4e9250175600-1.png" alt="1c5c680e-6b45-4438-a078-4e9250175600-1.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">对于这种情况我们应该先确认文件头（此处为<code>50 4B 03 04</code>），然后用Google查找这个文件头，看看这个格式是什么格式 （其实有个更取巧的办法，就是访问<a href="https://en.wikipedia.org/wiki/List_of_file_signatures" target="_blank" rel="noopener noreferrer nofollow ugc">List of file signatures</a>，然后直接Ctrl-F来进行查找）</p>
<p dir="auto"><img src="/assets/uploads/files/1731722392241-37c1893a-790e-4e1e-be24-d828ef6c4999-2.png" alt="37c1893a-790e-4e1e-be24-d828ef6c4999-2.png" class=" img-fluid img-markdown" /></p>
<blockquote>
<p dir="auto">zip file format and formats based on it, such as EPUB, JAR, ODF, OOXML</p>
</blockquote>
<p dir="auto">显然，我们的文件很有可能是用zip直接压缩而来，尝试使用7-zip打开</p>
<p dir="auto"><img src="/assets/uploads/files/1731722410889-5cdbabe0-ddd8-41f1-a338-774e3d3f9a55-3.png" alt="5cdbabe0-ddd8-41f1-a338-774e3d3f9a55-3.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">成功了，现在我们就完成了第一步</p>
<p dir="auto">把这些东西全部解压出来观察我们解压后的文件结构</p>
<p dir="auto"><img src="/assets/uploads/files/1731722426198-9c1dd084-f7b1-4d2c-9c91-42c81f65f433-4.png" alt="9c1dd084-f7b1-4d2c-9c91-42c81f65f433-4.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">可以发现我们的主要要分析的文件应该是<code>eboot.bin</code>，<code>xxx_info.psb.m</code>和<code>xxx_body.bin</code>，这是因为<code>movie</code>文件夹内的文件已经是我们希望的<code>.mp4</code>这种常见格式，我们不需要去动他，而另外两个文件夹内的文件过小（因为Galgame存在图片和音频等各种媒体资源，所以一般情况下存放资源文件的档案不可能很小），我们应该避免先分析这些文件。</p>
<p dir="auto">再根据文件大小 （<code>image_body.bin</code> 占 <code>636MB</code> 而 <code>image_info.psb.m</code> 仅仅只有 <code>120KB</code> ）我们可以推断出 <code>xxx_body.bin</code> 为资源文件的主体部分，<code>xxx_info.psb.m</code> 应该为主体部分的配置文件或者索引之类的东西。</p>
<p dir="auto">看到 <code>.bin</code> 这种东西，我们先用<code>GARbro</code>这种通用工具尝试（实际上<code>GARbro</code>无法解包），如果不行再尝试我们一开始说过的查找文件头的办法</p>
<p dir="auto"><img src="/assets/uploads/files/1731722443715-057b2b06-4441-4e6f-aada-d2ad9e2118e8-5.png" alt="057b2b06-4441-4e6f-aada-d2ad9e2118e8-5.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">可以看到文件头为<code>6D 64 66 00</code>，查找可知</p>
<p dir="auto"><img src="/assets/uploads/files/1731722462881-fdae700d-ae98-407e-9eed-7bd5a3da7e49-6.png" alt="fdae700d-ae98-407e-9eed-7bd5a3da7e49-6.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">这是一个<code>M2 Archive File</code>，知道了这些必要信息后，我们可以Google搜索关键词来查找工具，注意，第一次搜索关键词越多越好，找不到再删除一些不确定的关键词来搜索</p>
<p dir="auto">此处我们使用<code>*_body.bin mdf m2 archive</code>来搜索</p>
<p dir="auto"><img src="/assets/uploads/files/1731722476606-11e5d0b8-1cb8-4cb0-ab59-cacdf0c4af57-7.png" alt="11e5d0b8-1cb8-4cb0-ab59-cacdf0c4af57-7.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">可以看到，第二个搜索结果中与我们的关键词十分匹配，点开查看可以知道</p>
<blockquote>
<p dir="auto">The engine the Switch version runs on is "Kaleido ADV Workshop" by M2.<br />
There are tools to extract the *_body.bin and *_info.psb.m files, but you need an encryption key from the executable.</p>
</blockquote>
<p dir="auto">也就是说，这确实是一种叫做<code>M2 Engine</code>的引擎，解包他确实有特定的工具，但是我们需要知道这个密钥（贴子中没有指明具体的工具，所以我们重新查找<code>m2 engine .bin 解包</code>，这次因为我们已经知道了引擎名称，所以直接搜索）</p>
<p dir="auto"><img src="/assets/uploads/files/1731722496232-6a26448d-19b4-446b-9ec8-12d5be8982f3-8.png" alt="6a26448d-19b4-446b-9ec8-12d5be8982f3-8.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">出现了一个<code>GitHub</code>链接，点开查看这个issue，发现这个issue主要是在讨论解包<code>file_info.psb.m</code>遇到的问题，顺带我们得知这个项目可以解包我们的<code>M2 Archive File</code>（这是一个叫做<a href="https://github.com/UlyssesWu/FreeMote" target="_blank" rel="noopener noreferrer nofollow ugc">FreeMote</a>的项目）。</p>
<p dir="auto">既然找到工具了，那么这个时候我们应该返回项目主页查看具体的Readme.md也就是项目的介绍，通常此处会写明怎么使用这个项目（如果没有就翻翻Wiki之类的东西，再没有就去issue区慢慢研究怎么用...都没有的话，就只能请你自主阅读项目源码了），</p>
<blockquote>
<p dir="auto">FreeMote is a set of tool/libs for <code>M2 Packaged Struct Binary</code> file format. The file header usually starts with <code>PSB</code>/<code>PSZ</code>/<code>mdf</code>, and the file extensions usually are <code>.psb|.psz|.mdf|.pimg|.scn|.mmo|.emtbytes|.mtn|.dpak|.psb.m</code>.</p>
</blockquote>
<p dir="auto">显然，这个项目就是我们要找的工具，接着往下翻Readme可以看到</p>
<blockquote>
<p dir="auto">Read <a href="https://github.com/UlyssesWu/FreeMote/wiki/CommandLine-Usage-for-Tools" target="_blank" rel="noopener noreferrer nofollow ugc">wiki</a> for detailed usages.</p>
</blockquote>
<p dir="auto">接着查阅wiki可以得知</p>
<p dir="auto"><img src="/assets/uploads/files/1731722512013-97c24024-bcc4-4506-b1da-ebf02dd8ecfd-9.png" alt="97c24024-bcc4-4506-b1da-ebf02dd8ecfd-9.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">我们应该使用以下命令来解包我们的文件</p>
<pre><code class="language-shell">PsDecompile info-psb xxx_info.psb.m -k {key} -a
</code></pre>
<p dir="auto">那么我们的问题就变成了如何找到这个<code>key</code></p>
<p dir="auto">查阅wiki的<code>PSB-Shells,-Types,-Platforms</code>可以得知</p>
<blockquote>
<p dir="auto">Key: usually hex string (length = 13 for most cases, can be different for M2 games, such as 9, and there could be any string rather than just hex), e.g. 523aad2de7132, 38757621acf82, ae3bb93923bf8, Rj9Pegoh4<br />
Seed: key + file name, e.g. 523aad2de7132font_info.psb.m, 38757621acf82voice_info.psb.m</p>
</blockquote>
<p dir="auto">但是我们并没有找到查找<code>key</code>的办法，这种情况我们应该查找issue，看看有没有人问过类似的问题</p>
<p dir="auto"><img src="/assets/uploads/files/1731722532477-324ea802-5427-40c9-bb2e-76dc1ebe007a-10.png" alt="324ea802-5427-40c9-bb2e-76dc1ebe007a-10.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">在<a href="https://github.com/UlyssesWu/FreeMote/issues/93" target="_blank" rel="noopener noreferrer nofollow ugc">issue #93</a>下我们得知</p>
<blockquote>
<p dir="auto">你要去二进制文件（即包含程序逻辑的文件，比如PC游戏就是dll或者exe）里找key，解密的逻辑显然位于这种文件中。</p>
</blockquote>
<p dir="auto">也就是说，我们应该去<code>eboot.bin</code>下找（因为一番搜索得知<code>eboot.bin</code>是psv下的二进制文件）</p>
<p dir="auto">虽然我们知道<code>key</code>的结构，可以暴力枚举出所有可能的<code>key</code>（即<code>eboot.bin</code>下所有长度为13的字符串）来进行尝试，但这也太麻烦了，所以我们继续查找相关issue</p>
<p dir="auto"><img src="/assets/uploads/files/1731722550174-33b62de2-3c9b-42c5-a97d-61ecb1d9c4e0-11.png" alt="33b62de2-3c9b-42c5-a97d-61ecb1d9c4e0-11.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">虽然在<a href="https://github.com/UlyssesWu/FreeMote/issues/95" target="_blank" rel="noopener noreferrer nofollow ugc">issue #95</a>中我们已经得知了<code>key</code>的值为<code>a23e898ef1032</code>，但是我们想知道是找到<code>key</code>的方法</p>
<p dir="auto">继续翻阅相关issue，在<a href="https://github.com/UlyssesWu/FreeMote/issues/30" target="_blank" rel="noopener noreferrer nofollow ugc">issue #30</a>可以得知</p>
<p dir="auto"><img src="/assets/uploads/files/1731722569757-c9e18678-0fc0-4520-b0db-a9cd9b0c5fda-12.png" alt="c9e18678-0fc0-4520-b0db-a9cd9b0c5fda-12.png" class=" img-fluid img-markdown" /></p>
<blockquote>
<p dir="auto">密匙藏在同目录的eboot.bin的script/.script_info.psb.m前面，用十六进制软件直接搜索就行了</p>
</blockquote>
<p dir="auto">我们直接尝试搜索</p>
<p dir="auto"><img src="/assets/uploads/files/1731722584508-f419a545-47e8-4c75-9aee-c6f2ae3735d4-13.png" alt="f419a545-47e8-4c75-9aee-c6f2ae3735d4-13.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">果然找到了密钥，证实了我们<code>key</code>的值确实为<code>a23e898ef1032</code>。</p>
<p dir="auto">尝试运行</p>
<pre><code class="language-shell">PsbDecompile.exe info-psb -k a23e898ef1032 sound_info.psb.m -a
</code></pre>
<p dir="auto"><img src="/assets/uploads/files/1731722599319-b1afdc7c-3fb6-44fb-8522-5be1cac674ea-14.png" alt="b1afdc7c-3fb6-44fb-8522-5be1cac674ea-14.png" class=" img-fluid img-markdown" /><br />
<img src="/assets/uploads/files/1731722599338-4204e2d0-ab7b-47e7-8f72-70efffff4b05-15.png" alt="4204e2d0-ab7b-47e7-8f72-70efffff4b05-15.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">解包成功。</p>
<h2><a class="anchor-offset" name="总结"></a>总结</h2>
<p dir="auto">多问，多思考</p>
<p dir="auto"><s>顺带附带一个立绘合成的DLC</s></p>
<h2><a class="anchor-offset" name="dlc"></a>DLC</h2>
<p dir="auto">合成立绘的关键是正确解读配置文件，找到正确的拼接方法（大多为查找(x,y)坐标然后直接覆盖即可）</p>
<p dir="auto">此处以我们刚刚解包好的《ご注文はうさぎですか?? Wonderful Party!》（PSV）为例</p>
<p dir="auto"><img src="/assets/uploads/files/1731722624871-f8228f1a-9911-4586-9f83-eea1238c63a8-16.png" alt="f8228f1a-9911-4586-9f83-eea1238c63a8-16.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">显然，配置文件为<code>.json</code>，我们随便提取一个立绘组来进行研究</p>
<p dir="auto">此处以<code>tino_1cc1bc3c49e93177acc67700604fda8d</code>为例</p>
<p dir="auto"><code>tino_1cc1bc3c49e93177acc67700604fda8d</code>的配置文件有两个，一个为<code>tino_1c876bf3986382099796dd6ea88266cc.psb.m.json</code>另一个为<code>tino_1c876bf3986382099796dd6ea88266cc.psb.m.resx.json</code></p>
<p dir="auto"><code>tino_1c876bf3986382099796dd6ea88266cc.psb.m.json</code>:</p>
<pre><code class="language-json">{
  "crop": {
    "h": 574,
    "w": 319,
    "x": 1138,
    "y": 170
  },
  "eyediff": {
    "h": 55.0,
    "w": 104.0,
    "x": 1277.0,
    "y": 321.0
  },
  "eyediffbase": 320,
  "eyemap": {
    "目目そらし": null,
    "目目そらし1": 0,
    "目目そらし2": 1
  },
  "h": 744,
  "id": "image",
  "imageList": [{
      "height": 574,
      "label": "tino_1c876bf3986382099796dd6ea88266cc",
      "texture": [{
          "height": 512.0,
          "image": {
            "height": 512,
            "pixel": "#resource#1",
            "type": "RGBA8_SW",
            "width": 512
          },
          "left": 0.0,
          "top": 0.0,
          "width": 512.0
        },{
          "height": 64.0,
          "image": {
            "height": 64,
            "pixel": "#resource#0",
            "type": "RGBA8_SW",
            "width": 512
          },
          "left": 0.0,
          "top": 512.0,
          "width": 512.0
        }],
      "width": 437
    }],
  "label": "",
  "lipdiff": {
    "h": 11.0,
    "w": 8.0,
    "x": 1332.0,
    "y": 394.0
  },
  "lipdiffbase": 427,
  "lipmap": {
    "口目そらし": 0,
    "口目そらし1": 1,
    "口目そらし2": 2
  },
  "spec": "vita",
  "version": 1.0,
  "w": 2560
}
</code></pre>
<p dir="auto"><code>tino_1c876bf3986382099796dd6ea88266cc.psb.m.resx.json</code>:</p>
<pre><code class="language-json">{
  "PsbVersion": 3,
  "PsbType": "Tachie",
  "Platform": "vita",
  "CryptKey": null,
  "ExternalTextures": false,
  "Context": {
    "MdfKeyLength": 131,
    "FileName": "tino_1c876bf3986382099796dd6ea88266cc.psb.m",
    "MdfKey": "a23e898ef1032tino_1c876bf3986382099796dd6ea88266cc.psb.m",
    "PsbZlibFastCompress": false,
    "PsbShellType": "MDF"
  },
  "Resources": {
    "0": "tino_1c876bf3986382099796dd6ea88266cc.psb.m/tino_1c876bf3986382099796dd6ea88266cc.png",
    "1": "tino_1c876bf3986382099796dd6ea88266cc.psb.m/tino_1c876bf3986382099796dd6ea88266cc.png"
  }
}
</code></pre>
<p dir="auto">通过观察可以知道我们应该具体研究<code>tino_1c876bf3986382099796dd6ea88266cc.psb.m.json</code>。因为<code>tino_1c876bf3986382099796dd6ea88266cc.psb.m.resx.json</code>显然没有我们需要的数据。</p>
<p dir="auto">接着我们看看图片</p>
<p dir="auto"><img src="/assets/uploads/files/1731722640190-db6c2c39-aa19-41ad-affd-b082d0dba81b-tino_1cc1bc3c49e93177acc67700604fda8d.png" alt="db6c2c39-aa19-41ad-affd-b082d0dba81b-tino_1cc1bc3c49e93177acc67700604fda8d.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">观察可知，所有的差分嵌在一张图片上，那么我们就需要把这些差分裁剪下来，然后再把基底裁剪出来进行覆盖</p>
<p dir="auto">假如我们知道我们需要裁剪图片的左上角的坐标(x,y)，以及他的长宽，我们就可以把这张图片裁剪出来</p>
<p dir="auto">也就是说我们需要构造函数 <code>cutimg(cv::Mat img, int x, int y, int w, int h) -&gt; cv::Mat</code></p>
<p dir="auto">然后通过读取配置文件我们可以很容易的获得这些参数，从而裁剪出所有差分和基底</p>
<p dir="auto">接着我们只需要把差分覆盖在基底上，把文件输出即可，这个过程我们只需要知道这两张图片的相对坐标即可</p>
<p dir="auto">也就是说我们还需要构造这么一个函数 <code>coverimg(cv::Mat baseimg, cv::Mat faceimg, int x, int y) -&gt; cv::Mat</code></p>
<p dir="auto">这两个参数也可以通过读取配置文件然后进行一些简单的运算得出（通常为二者坐标之差取绝对值）</p>
<p dir="auto">最后附上我的屎山（）</p>
<pre><code class="language-cpp">#include &lt;json/json.h&gt;
#include &lt;fstream&gt;
#include &lt;filesystem&gt;
#include &lt;json/value.h&gt;
#include &lt;vector&gt;
#include &lt;opencv4/opencv2/opencv.hpp&gt;
#include &lt;windows.h&gt;

namespace fs = std::filesystem;

struct cimg{
    int x, y, n;
    std::string name;
};

void createcimg(std::string val, Json::Value &amp;root, std::vector&lt;cimg&gt; &amp;img);

void fixdcimg(std::vector&lt;cimg&gt; &amp;img, int w, int h, int a, int basex);

Json::Value readjson(std::string file);

cv::Mat cutimg(cv::Mat img, int x, int y, int w, int h);

cv::Mat coverimg(cv::Mat baseimg, cv::Mat faceimg, int x, int y);

void work(std::string pngfile, std::string inputfile, std::string basename);

int main(int argc,char* argv[]){
    fs::create_directory("output");
    for (const auto&amp; entry : fs::directory_iterator(".")) {
        if (fs::is_regular_file(entry.status())) {
            fs::path file_path = entry.path();
            std::string filename = file_path.filename().string();
            if (filename.find(".psb.m.json") != std::string::npos) {
                std::string inputfile = filename;
                std::string basename = filename.substr(0,filename.size() - 11);
                std::string pngfile = "./" + basename + ".psb.m/" + basename + ".png";
                work(pngfile, inputfile, basename);
            }
        }
    }
    return 0;
}

Json::Value readjson(std::string file){
    std::ifstream jsonfile(file, std::ifstream::binary);
    Json::Value root;
    Json::CharReaderBuilder readerBuilder;
    std::string errs;
    Json::parseFromStream(readerBuilder, jsonfile, &amp;root, &amp;errs);
    return root;
}

void createcimg(std::string val,Json::Value &amp;root,std::vector&lt;cimg&gt; &amp;img){
    Json::Value::Members members;  
    members = root[val.c_str()].getMemberNames();
    int num = 0;
    for (Json::Value::Members::iterator iterMember = members.begin(); iterMember != members.end(); iterMember++){
        cimg temp;
        std::string strKey = *iterMember;
        if (root[val.c_str()][strKey.c_str()].isNull())  {  
            temp.name = strKey.c_str();
            temp.n = -1;
            num--;
        }
        else{
            temp.name = strKey.c_str();
            temp.n = num;
        }
        img.push_back(temp);
        num ++;
    }
    return ;
}

void fixdcimg(std::vector&lt;cimg&gt; &amp;img, int w, int h, int a, int basex){
    for (int i = 0; i &lt; img.size(); i++){
        if(img[i].n == -1){
            img[i].x = -1;
            img[i].y = -1;
        }
        else{
            img[i].x = basex + (img[i].n / a) * w;
            img[i].y = (img[i].n % a) * h;
        }
    }
}

cv::Mat cutimg(cv::Mat img, int x, int y, int w, int h){
    cv::Rect cropRegion(x, y, w, h);
    cropRegion = cropRegion &amp; cv::Rect(0, 0, img.cols, img.rows);
    return img(cropRegion).clone();
}

cv::Mat coverimg(cv::Mat baseimg, cv::Mat faceimg, int x, int y){
    cv::Mat output = baseimg.clone();
    for (int i = 0; i &lt; faceimg.rows; ++i) {
        for (int j = 0; j &lt; faceimg.cols; ++j) {
            int targetX = x + j;
            int targetY = y + i;
            if (targetX &gt;= 0 &amp;&amp; targetX &lt; baseimg.cols &amp;&amp; targetY &gt;= 0 &amp;&amp; targetY &lt; baseimg.rows) {
                cv::Vec4b facePixel = faceimg.at&lt;cv::Vec4b&gt;(i, j);
                if (facePixel[3] &gt; 0) {
                    output.at&lt;cv::Vec4b&gt;(targetY, targetX) = facePixel;
                }
            }
        }
    }
    return output;
}

void work(std::string pngfile, std::string inputfile, std::string basename){

    Json::Value root = readjson(inputfile);
    int eyex = abs(root["crop"]["x"].asInt() - root["eyediff"]["x"].asInt()) -1;
    int eyey = abs(root["crop"]["y"].asInt() - root["eyediff"]["y"].asInt()) -1;
    
    int h = root["crop"]["h"].asInt(),
        w = root["crop"]["w"].asInt(),
        eyeh = root["eyediff"]["h"].asInt() + 2,
        eyew = root["eyediff"]["w"].asInt() + 2,
        eyediffbase = root["eyediffbase"].asInt(),
        eyediffend = root["h"].asInt();
    
    
    std::vector&lt;cimg&gt; eyemap;
    createcimg("eyemap",root,eyemap);
    fixdcimg(eyemap,eyew,eyeh,h/eyeh,eyediffbase);
    
    int lipx, lipy, liph, lipw, lipdiffbase;
    std::vector&lt;cimg&gt; lipmap;
    createcimg("lipmap",root,lipmap);

    if(root["lipdiffbase"].isNull()) {
        lipx = -1,
        lipy = -1,
        liph = -1,
        lipw = -1,
        lipdiffbase = -1;
    }
    else{
        lipx = abs(root["crop"]["x"].asInt() - root["lipdiff"]["x"].asInt()),
        lipy = abs(root["crop"]["y"].asInt() - root["lipdiff"]["y"].asInt()),
        liph = root["lipdiff"]["h"].asInt() + 2,
        lipw = root["lipdiff"]["w"].asInt() + 2,
        lipdiffbase = root["lipdiffbase"].asInt();
        fixdcimg(lipmap, lipw, liph, h/liph, lipdiffbase);
    }
    
    cv::Mat img = cv::imread(pngfile, cv::IMREAD_UNCHANGED);
    int num = eyemap.size();
    cv::Mat baseimg = cutimg(img, 0, 0, w, h);
    for(int i = 0; i &lt; num; i++){
        cv::Mat out;
        if(eyemap[i].n != -1 &amp;&amp; lipmap[i].n != -1){
            cv::Mat eyecut = cutimg(img, eyemap[i].x, eyemap[i].y, eyew, eyeh);
            cv::Mat lipcut = cutimg(img, lipmap[i].x, lipmap[i].y, lipw, liph);
            out = coverimg(baseimg, eyecut, eyex, eyey);
            out = coverimg(out, lipcut, lipx, lipy);
        }
        else if(eyemap[i].n != -1 &amp;&amp; lipmap[i].n == -1){
            cv::Mat eyecut = cutimg(img, eyemap[i].x, eyemap[i].y, eyew, eyeh);
            out = coverimg(baseimg, eyecut, eyex, eyey);
        }
        else{
            out = baseimg;
        }

        fs::path filename = "./output/"+ basename + "_" + eyemap[i].name + "_" + lipmap[i].name + ".png";
        cv::imwrite(filename.string(), out);
    }
    
}
</code></pre>
]]></description><link>https://galgame.dev/topic/17395/如何正确找到解包游戏的工具</link><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 18:10:00 GMT</lastBuildDate><atom:link href="https://galgame.dev/topic/17395.rss" rel="self" type="application/rss+xml"/><pubDate>Sat, 16 Nov 2024 02:04:45 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to 如何正确找到解包游戏的工具 on Tue, 01 Apr 2025 06:10:20 GMT]]></title><description><![CDATA[<p dir="auto">怎么说呢，对于有一定程序基础的人来说还是不错的指南。<br />
但是我觉得对于90%的当代网友，你让他学会正确解压分卷压缩包都困难，更不能指望他能学会解包了。。</p>
]]></description><link>https://galgame.dev/post/66642</link><guid isPermaLink="true">https://galgame.dev/post/66642</guid><dc:creator><![CDATA[Yukie]]></dc:creator><pubDate>Tue, 01 Apr 2025 06:10:20 GMT</pubDate></item></channel></rss>