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 }