neptools

Modding tools to Neptunia games
git clone https://git.neptards.moe/neptards/neptools.git
Log | Files | Refs | Submodules | README | LICENSE

cpk.cpp (8186B)


      1 #include "vita_plugin/cpk.hpp"
      2 
      3 #include "format/stcm/file.hpp"
      4 #include "open.hpp"
      5 #include "pattern_parse.hpp"
      6 #include "sink.hpp"
      7 #include "source.hpp"
      8 #include "txt_serializable.hpp"
      9 #include "vita_plugin/taihen_cpp.hpp"
     10 
     11 #include <libshit/memory_utils.hpp>
     12 
     13 #include <cstdint>
     14 #include <boost/filesystem/operations.hpp>
     15 #include <fstream>
     16 #include <psp2/kernel/modulemgr.h>
     17 #include <stdexcept>
     18 
     19 #define LIBSHIT_LOG_NAME "cpk"
     20 #include <libshit/logger_helper.hpp>
     21 
     22 namespace Neptools::VitaPlugin
     23 {
     24 
     25   namespace
     26   {
     27     struct FsBinderHandle
     28     {
     29       FsBinderHandle* parent;
     30       FsBinderHandle* left;
     31       FsBinderHandle* right;
     32       void* unk0;
     33       std::uint32_t unk1;
     34       std::uint32_t index;
     35       std::uint32_t usage;
     36       // ...
     37     };
     38 
     39     struct FileHandle
     40     {
     41       std::int32_t fd;
     42       // ...
     43     };
     44 
     45     struct FileCpkData
     46     {
     47       FileHandle* fhan;
     48       const char* cpk_name;
     49       std::uint64_t offs;
     50       std::uint32_t compr_size, uncompr_size;
     51       std::uint32_t binder_index;
     52     };
     53 
     54     struct FileInfo
     55     {
     56       FileCpkData* dat;
     57       char* name;
     58       std::uint32_t id;
     59       std::uint32_t id_ignored;
     60       FileHandle* fhan;
     61       const char* cpk_name;
     62       std::uint64_t cpk_offs;
     63       std::uint32_t idx1, idx2;
     64       std::uint32_t chain_length;
     65       // ...
     66     };
     67   }
     68 
     69   static std::string data_path;
     70   static std::string cache_path;
     71 
     72   using DecompressFun = uint64_t (*)(char*, char*, uint64_t, char*, uint64_t);
     73   static DecompressFun decompress;
     74 
     75   static Source GetSource(const FileCpkData& dat)
     76   {
     77     auto src = Source::FromFd(dat.cpk_name, dat.fhan->fd, false);
     78     src.Slice(dat.offs, dat.compr_size);
     79     if (dat.compr_size == dat.uncompr_size) return src;
     80 
     81     auto buf = Libshit::MakeUnique<char[]>(dat.uncompr_size, Libshit::uninitialized);
     82     src.Pread(0, buf.get(), dat.compr_size);
     83 
     84     char tmp[0x104];
     85     auto res = decompress(tmp, buf.get(), dat.compr_size, buf.get(),
     86                           dat.uncompr_size);
     87     if (res != dat.uncompr_size)
     88       LIBSHIT_THROW(std::runtime_error, "Data decompression failed");
     89 
     90     return Source::FromMemory("", Libshit::Move(buf), dat.uncompr_size);
     91   }
     92 
     93   static std::pair<const char*, size_t> DoTxt(
     94     const char* fname, const FileCpkData& dat)
     95   {
     96     auto cpth = cache_path + fname;
     97     if (boost::filesystem::exists(cpth))
     98     {
     99       DBG(3) << "Cached exists: " << cpth << std::endl;
    100       return {cache_path.c_str(), boost::filesystem::file_size(cpth)};
    101     }
    102 
    103     auto pth = data_path + fname + ".txt";
    104     if (!boost::filesystem::exists(pth)) return {nullptr, 0};
    105 
    106     auto dir = boost::filesystem::path(cpth).remove_filename();
    107     DBG(4) << "Mkdir " << dir << std::endl;
    108     boost::filesystem::create_directories(dir);
    109 
    110     DBG(3) << "Opening original " << pth << std::endl;
    111     auto src = GetSource(dat);
    112 
    113     if (CHECK_DBG(4)) src.Dump(*Sink::ToFile(cpth + ".orig", src.GetSize()));
    114 
    115     auto dump = OpenFactory::Open(src);
    116     DBG(4) << "GetTxt" << std::endl;
    117     auto txt = dump->GetDefaultTxtSerializable(dump);
    118     DBG(4) << "Importing..." << std::endl;
    119     txt->ReadTxt(OpenIn(pth));
    120     DBG(4) << "Gc..." << std::endl;
    121     if (auto f = dynamic_cast<Stcm::File*>(txt.get())) f->Gc();
    122     DBG(4) << "Fixup..." << std::endl;
    123     dump->Fixup();
    124     DBG(4) << "Dump..." << std::endl;
    125     dump->Dump(Libshit::Move(cpth));
    126 
    127     return {cache_path.c_str(), dump->GetSize()};
    128   }
    129 
    130   static std::pair<const char*, size_t> DoBin(const char* fname)
    131   {
    132     auto pth = data_path + fname;
    133     if (!boost::filesystem::exists(pth)) return {nullptr, 0};
    134     DBG(3) << "Simple exists " << pth << std::endl;
    135     return {data_path.c_str(), boost::filesystem::file_size(pth)};
    136   }
    137 
    138   using FsBinderHandleCreateFun = int(*)(FsBinderHandle**);
    139   static FsBinderHandleCreateFun fs_binder_handle_create;
    140 
    141   static FileHandle* dir_fhan = reinterpret_cast<FileHandle*>(0x8143ada0);
    142 
    143   static TaiHook<int(FsBinderHandle*, FileInfo*, FsBinderHandle**, int*)>
    144   get_file_info_hook;
    145   static FsBinderHandle* my_han;
    146 
    147   static int HookGetFileInfo(
    148     FsBinderHandle* han, FileInfo* fi, FsBinderHandle** han_out, int* succ)
    149   {
    150     auto ret = get_file_info_hook(han, fi, han_out, succ);
    151 
    152     if (ret != 0 || *succ != 1 || !fi->dat) return ret;
    153     if (!my_han)
    154     {
    155       DBG(0) << "Creating FsBinder handle: " << fs_binder_handle_create(&my_han)
    156              << std::endl;
    157       fs_binder_handle_create(&my_han);
    158       if (!my_han) return ret;
    159       my_han->usage = 3;
    160     }
    161 
    162     try
    163     {
    164       DBG(2) << "Checking file: " << fi->name << std::endl;
    165 
    166       auto r = DoBin(fi->name);
    167       if (!r.first) r = DoTxt(fi->name, *fi->dat);
    168       if (!r.first) return ret;
    169 
    170       DBG(1) << "Hooked file: " << fi->name << " to " << r.first
    171              << ", size: " << r.second <<  std::endl;
    172 
    173       fi->dat->fhan = dir_fhan;
    174       fi->dat->cpk_name = r.first;
    175       fi->dat->offs = 0;
    176       fi->dat->compr_size = r.second;
    177       fi->dat->uncompr_size = r.second;
    178       fi->dat->binder_index = my_han->index;
    179 
    180       fi->cpk_name = r.first;
    181       fi->cpk_offs = 0;
    182       fi->fhan = dir_fhan;
    183       fi->idx1 = fi->idx2 = my_han->index;
    184       if (han_out) *han_out = my_han;
    185 
    186       return ret;
    187     }
    188     catch (...)
    189     {
    190       ERR << Libshit::PrintException(true) << std::endl;
    191       abort();
    192     }
    193   }
    194 
    195   static auto GET_FILE_INFO_PATTERN = NEPTOOLS_PATTERN(
    196     "2d e9 30/bf 4f c0/f0 b0");
    197 
    198   static auto DECOMPRESS_PATTERN = NEPTOOLS_PATTERN(
    199     "2d e9 f0 43 83 b0 0a 9e");
    200 
    201   static auto FS_BINDER_HANDLE_CREATE_PATTERN = NEPTOOLS_PATTERN(
    202     "70 b5 04 1c 01 d0");
    203 
    204   static auto DIR_FHAN_PATTERN = NEPTOOLS_PATTERN(
    205     "4a/f0 f6/fb a0/00 5e/8f c8/f0 f2/fb 43/00 1e/8f b9 f1 00 0f 12 d0 c9 f8 "
    206     "00 e0");
    207 
    208   template <typename T>
    209   static T ThumbPtr(const Byte* ptr) noexcept
    210   { return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(ptr)|1); }
    211 
    212   static uint16_t GetThumbImm16(const uint16_t* ptr) noexcept
    213   {
    214     auto b0 = ptr[0], b1 = ptr[1];
    215     return ((b0 & 0x000f) << 12) | ((b0 & 0x0400) << 1) |
    216       ((b1 & 0x7000) >> 4) | (b1 & 0x00ff);
    217   }
    218 
    219   void Init(std::string data_path_in, std::string cache_path_in)
    220   {
    221     data_path = Libshit::Move(data_path_in);
    222     cache_path = Libshit::Move(cache_path_in);
    223 
    224     INF << "Data path: " << data_path
    225         << "\nCache path: " << cache_path << std::endl;
    226 
    227     DBG(0) << "Erasing cache..." << std::endl;
    228     boost::filesystem::remove_all(cache_path);
    229 
    230     tai_module_info_t tai_info;
    231     tai_info.size = sizeof(tai_info);
    232     auto ret = taiGetModuleInfo(TAI_MAIN_MOD_STR, &tai_info);
    233     if (ret < 0)
    234       LIBSHIT_THROW(TaiError, "taiGetModuleInfo failed", "Error code", ret);
    235 
    236     SceKernelModuleInfo kern_info;
    237     kern_info.size = sizeof(kern_info);
    238     ret = sceKernelGetModuleInfo(tai_info.modid, &kern_info);
    239     if (ret < 0)
    240       LIBSHIT_THROW(TaiError, "sceKernelGetModuleInfo failed", "Error code", ret);
    241 
    242     Libshit::StringView seg0{static_cast<char*>(kern_info.segments[0].vaddr),
    243                              kern_info.segments[0].memsz};
    244     DBG(1) << "Base: " << static_cast<const void*>(seg0.data())
    245            << ", size: " << seg0.size() << std::endl;
    246 
    247     DBG(2) << "Finding GET_FILE_INFO" << std::endl;
    248     auto get_file_info_addr = GET_FILE_INFO_PATTERN.Find(seg0);
    249 
    250     DBG(2) << "Finding DECOMPRESS" << std::endl;
    251     decompress = ThumbPtr<DecompressFun>(DECOMPRESS_PATTERN.Find(seg0));
    252     DBG(2) << "Finding FS_BINDER_HANDLE_CREATE" << std::endl;
    253     fs_binder_handle_create = ThumbPtr<FsBinderHandleCreateFun>(
    254       FS_BINDER_HANDLE_CREATE_PATTERN.Find(seg0));
    255 
    256     DBG(2) << "Finding DIR_FHAN" << std::endl;
    257     auto dir_fhan_info = reinterpret_cast<const uint16_t*>(
    258       DIR_FHAN_PATTERN.Find(seg0));
    259     dir_fhan = reinterpret_cast<FileHandle*>(
    260       GetThumbImm16(dir_fhan_info) | (GetThumbImm16(dir_fhan_info+2) << 16));
    261     DBG(3) << "dir_fhan -> " << dir_fhan << std::endl;
    262 
    263     ret = taiHookFunctionOffset(
    264       get_file_info_hook, tai_info.modid, 0,
    265       get_file_info_addr - static_cast<Byte*>(kern_info.segments[0].vaddr), 1,
    266       reinterpret_cast<void*>(&HookGetFileInfo));
    267 
    268     if (ret < 0)
    269       LIBSHIT_THROW(TaiError, "Hook GetFileInfo failed", "Error code", ret);
    270   }
    271 
    272 }