neptools

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

cpk.cpp (9268B)


      1 #include "cpk.hpp"
      2 #include "hook.hpp"
      3 #include "../pattern_parse.hpp"
      4 
      5 #include "../format/cl3.hpp"
      6 #include "../format/stcm/file.hpp"
      7 #include "../format/stcm/gbnl.hpp"
      8 
      9 #include <boost/filesystem/operations.hpp>
     10 #include <iostream>
     11 #include <fstream>
     12 
     13 #define LIBSHIT_LOG_NAME "cpk"
     14 #include <libshit/logger_helper.hpp>
     15 
     16 namespace Neptools
     17 {
     18 
     19   static auto OPEN_FILE  = NEPTOOLS_PATTERN(
     20     "55 8b ec 6a ff 68 ?? ?? ?? ?? 64 a1 00 00 00 00 50 81 ec 3c 06 00 00");
     21 
     22   static auto FILE_CLOSE = NEPTOOLS_PATTERN(
     23     "55 8b ec 6a ff 68 ?? ?? ?? ?? 64 a1 00 00 00 00 50 51 53 56 "
     24     "57 a1 ?? ?? ?? ?? 33 c5 50 8d 45 f4 64 a3 00 00 00 00 8b f9 "
     25     "8d 77 38 56 89 75 f0 ff 15 ?? ?? ?? ?? c7 45 fc 00 00 00 00 "
     26     "8b 47 10");
     27   static auto FILE_CLOSE_RB1PATCH = NEPTOOLS_PATTERN(
     28     "55 8b ec 53 56 57 8b f9 8d 77 38 56 ff 15 ?? ?? ?? ?? 8b 4f 0c");
     29 
     30   static auto FILE_READ = NEPTOOLS_PATTERN(
     31     "55 8b ec 6a ff 68 ?? ?? ?? ?? 64 a1 00 00 00 00 50 51 53 56 "
     32     "57 a1 ?? ?? ?? ?? 33 c5 50 8d 45 f4 64 a3 00 00 00 00 8b d9 "
     33     "8d 73 38 56 89 75 f0 ff 15 ?? ?? ?? ?? c7 45 fc 00 00 00 00 "
     34     "8b 55 08");
     35   static auto FILE_READ_RB1PATCH = NEPTOOLS_PATTERN(
     36     "55 8b ec 6a ff 68 ?? ?? ?? ?? 64 a1 00 00 00 00 50 51 53 56 57 a1 ?? ?? "
     37     "?? ?? 33 c5 50 8d 45 f4 64 a3 00 00 00 00 8b d9 8d 73 38 56 89 75 f0 ff "
     38     "15 ?? ?? ?? ?? 8b 55 08");
     39 
     40   // default size used by if
     41   constexpr const FileMemSize CPK_CHUNK = 128*1024*1024;
     42 
     43   namespace
     44   {
     45     struct CpkSource : public Source::Provider
     46     {
     47       CpkSource(boost::filesystem::path fname, CpkHandler* cpk, size_t index);
     48       ~CpkSource();
     49       void Pread(FilePosition offs, Byte* buf, FileMemSize len) override;
     50 
     51       CpkHandler* cpk;
     52       size_t index;
     53     };
     54   }
     55 
     56   CpkSource::CpkSource(
     57     boost::filesystem::path fname, CpkHandler* cpk, size_t index)
     58     : Source::Provider{
     59         std::move(fname), cpk->entry_vect[index]->entry.uncompressed_size},
     60       cpk{cpk}, index{index}
     61   {}
     62 
     63   CpkSource::~CpkSource()
     64   {
     65     for (auto& e : lru)
     66       delete[] e.ptr;
     67     cpk->OrigCloseFile(index);
     68   }
     69 
     70   void CpkSource::Pread(FilePosition offs, Byte* buf, FileMemSize len)
     71   {
     72     if (len > CPK_CHUNK)
     73     {
     74       size_t read;
     75       cpk->entry_vect[index]->read_pos = offs;
     76       if (!cpk->OrigRead(index, reinterpret_cast<char*>(buf), len, &read))
     77         LIBSHIT_THROW(CpkError, "Cpk::OrigRead failed",
     78                       "Cpk error code", cpk->last_error);
     79       LIBSHIT_ASSERT(read == len);
     80       return;
     81     }
     82 
     83     do
     84     {
     85       auto coffs = offs / CPK_CHUNK * CPK_CHUNK;
     86       auto csize = std::min<FilePosition>(CPK_CHUNK, size-coffs);
     87       std::unique_ptr<char[]> cbuf{new char[csize]};
     88 
     89       size_t read;
     90       cpk->entry_vect[index]->read_pos = coffs;
     91       if (!cpk->OrigRead(index, cbuf.get(), csize, &read))
     92         LIBSHIT_THROW(CpkError, "Cpk::OrigRead failed",
     93                       "Cpk error code", cpk->last_error);
     94       LIBSHIT_ASSERT(read == csize);
     95 
     96       auto to_offs = offs % CPK_CHUNK;
     97       auto to_copy = std::min<FilePosition>(len, csize - to_offs);
     98       memcpy(buf, cbuf.get() + to_offs, to_copy);
     99       delete[] lru[lru.size()-1].ptr;
    100       LruPush(reinterpret_cast<Byte*>(cbuf.release()), offs, csize);
    101 
    102       buf += to_copy;
    103       offs += to_copy;
    104       len -= to_copy;
    105     }
    106     while (len);
    107   }
    108 
    109   CpkHandler::OpenFilePtr CpkHandler::orig_open_file;
    110   CpkHandler::CloseFilePtr CpkHandler::orig_close_file;
    111   CpkHandler::ReadPtr CpkHandler::orig_read;
    112 
    113   char CpkHandler::OpenFile(const char* fname, size_t* out)
    114   {
    115     DBG(2) << "OpenFile " << basename << fname;
    116     boost::filesystem::path pth{"neptools"};
    117     pth /= basename;
    118     pth /= fname;
    119 
    120     if (OpenTxtFile(fname, pth, out) || OpenFsFile(fname, pth, out))
    121     {
    122       DBG(2) << " -- hooked it" << std::endl;
    123       return 1;
    124     }
    125     else
    126     {
    127       auto ret = (this->*orig_open_file)(fname, out);
    128       DBG(2) << " -> " << int(ret) << ", " << *out << std::endl;
    129       return ret;
    130     }
    131   }
    132 
    133   bool CpkHandler::OpenFsFile(
    134     const char* fname, const boost::filesystem::path& pth, size_t* out)
    135   {
    136     auto h = CreateFileW(pth.c_str(), GENERIC_READ, FILE_SHARE_READ, 0,
    137                          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    138     if (h == INVALID_HANDLE_VALUE)
    139     {
    140       DBG(2) << " -- can't fs hook: CreateFileW failed: " << GetLastError();
    141       return false;
    142     }
    143     size_t size = GetFileSize(h, nullptr);
    144     if (size == INVALID_FILE_SIZE)
    145     {
    146       DBG(2) << " -- can't fs hook: GetFileSize failed: " << GetLastError();
    147       CloseHandle(h);
    148       return false;
    149     }
    150 
    151     // success
    152     auto& info = GetEntryVect(out);
    153     info.handle = h;
    154     strncpy(info.entry.file_name, fname, 260);
    155     info.entry.compressed_size = size;
    156     info.entry.uncompressed_size = size;
    157 
    158     return true;
    159   }
    160 
    161   bool CpkHandler::OpenTxtFile(
    162     const char* fname, const boost::filesystem::path& pth, size_t* out)
    163   {
    164     FileMemSize size;
    165     std::unique_ptr<Byte[]> buf;
    166 
    167     try
    168     {
    169       boost::filesystem::path pthtxt = pth;
    170       if (!boost::filesystem::exists(pthtxt += ".txt")) return false;
    171 
    172       Source src{boost::filesystem::exists(pth) ?
    173           Source::FromFile(pth) : GetSource(fname)};
    174       src.CheckSize(4);
    175 
    176       char hdr_buf[4];
    177       src.Pread(0, hdr_buf, 4);
    178 
    179       Libshit::SmartPtr<Dumpable> dmp;
    180       TxtSerializable* txt;
    181       if (memcmp(hdr_buf, "CL3L", 4) == 0)
    182       {
    183         dmp = Libshit::MakeSmart<Cl3>(src);
    184         txt = &static_cast<Cl3*>(dmp.get())->GetStcm();
    185       }
    186       else
    187       {
    188         dmp = Libshit::MakeSmart<Gbnl>(src);
    189         txt = static_cast<Gbnl*>(dmp.get());
    190       }
    191 
    192       txt->ReadTxt(OpenIn(pthtxt));
    193       dmp->Fixup();
    194       size = dmp->GetSize();
    195       buf.reset(new Byte[size]);
    196       dmp->Dump(MemorySink{buf.get(), size});
    197     }
    198     catch (const std::exception& e)
    199     {
    200       auto except = Libshit::ExceptionToString(false);
    201       DBG(2) << " -- txt hook failed: " << except << std::endl;
    202 
    203       std::stringstream ss;
    204       ss << "Failed to import " << pth << ", ignoring.\n\n" << except;
    205       MessageBoxA(nullptr, ss.str().c_str(), "Neptools", MB_OK | MB_ICONERROR);
    206       return false;
    207     }
    208 
    209     //dbg
    210     /*
    211     std::string pths = pth.string();
    212     boost::replace_all(pths, "/", "_");
    213     boost::replace_all(pths, "\\", "_");
    214     std::ofstream os{pths, std::ios_base::binary};
    215     os.write(reinterpret_cast<char*>(buf.get()), size);
    216     */
    217     //end dbg
    218 
    219     auto& info = GetEntryVect(out);
    220     strncpy(info.entry.file_name, fname, 260);
    221     info.handle = INVALID_HANDLE_VALUE;
    222     info.entry.compressed_size = size;
    223     info.entry.uncompressed_size = size;
    224     info.block = buf.release();
    225     return true;
    226   }
    227 
    228   CpkHandlerFileInfo& CpkHandler::GetEntryVect(size_t* out)
    229   {
    230     auto it = std::find_if(entry_vect.begin(), entry_vect.end(),
    231                            [](auto& x) { return x->is_valid == 0; });
    232     if (it == entry_vect.end())
    233     {
    234       entry_vect.reserve(entry_vect.size() + 1);
    235       auto ne = new CpkHandlerFileInfo;
    236       ne->huffmann_hdr = nullptr;
    237       ne->block = nullptr;
    238       entry_vect.push_back(ne);
    239       it = entry_vect.end() - 1;
    240     }
    241 
    242     (*it)->index = 0;
    243     (*it)->is_valid = true;
    244     (*it)->entry.field_000 = 0;
    245     (*it)->entry.file_index = 0;
    246     (*it)->entry.field_10c = 0;
    247     (*it)->entry.is_compressed = 0;
    248     (*it)->entry.field_11c = 0;
    249     (*it)->data_start = 0;
    250     (*it)->read_pos = 0;
    251     (*it)->decoded_block_index = -1;
    252 
    253     if (out) *out = it - entry_vect.begin();
    254     return **it;
    255   }
    256 
    257   char CpkHandler::CloseFile(unsigned index)
    258   {
    259     DBG(3) << "CloseFile " << index << std::endl;
    260     return OrigCloseFile(index);
    261   }
    262 
    263   char CpkHandler::Read(unsigned index, char* dst, size_t dst_size,
    264                         size_t* out_size_read)
    265   {
    266     DBG(3) << "Read " << index << " " << dst_size << std::endl;
    267 
    268     auto& en = *entry_vect[index];
    269     if (en.handle == INVALID_HANDLE_VALUE)
    270     {
    271       // cpk from mem
    272       DBG(3) << "cpk read " << en.read_pos << " " << dst_size << std::endl;
    273 
    274       dst_size = std::min(dst_size, en.entry.uncompressed_size - en.read_pos);
    275       memcpy(dst, static_cast<char*>(en.block) + en.read_pos, dst_size);
    276       en.read_pos += dst_size;
    277       if (out_size_read) *out_size_read = dst_size;
    278       return 1;
    279     }
    280     return OrigRead(index, dst, dst_size, out_size_read);
    281   }
    282 
    283   Source CpkHandler::GetSource(const char* fname)
    284   {
    285     size_t index;
    286     if (!OrigOpenFile(fname, &index))
    287       LIBSHIT_THROW(CpkError, "Cpk::OrigOpenFile failed",
    288                     "Cpk error code", last_error);
    289     try { return Source(Libshit::MakeSmart<CpkSource>(fname, this, index)); }
    290     catch (...)
    291     {
    292       OrigCloseFile(index);
    293       throw;
    294     }
    295   }
    296 
    297   void CpkHandler::Init()
    298   {
    299     DBG(1) << "Finding OPEN_FILE" << std::endl;
    300     orig_open_file = Hook(FindImage(OPEN_FILE), &CpkHandler::OpenFile, 5);
    301 
    302     DBG(1) << "Finding FILE_CLOSE" << std::endl;
    303     auto offs = MaybeFindImage(FILE_CLOSE);
    304     if (!offs) offs = FindImage(FILE_CLOSE_RB1PATCH);
    305     orig_close_file = Hook(offs, &CpkHandler::CloseFile, 5);
    306 
    307     DBG(1) << "Finding FILE_READ" << std::endl;
    308     offs = MaybeFindImage(FILE_READ);
    309     if (!offs) offs = FindImage(FILE_READ_RB1PATCH);
    310     orig_read = Hook(offs, &CpkHandler::Read, 5);
    311   }
    312 
    313 }