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 }