| 364 | } |
| 365 | |
| 366 | bool ExtractPSH(const std::string &psh_path, const std::string &output_path) { |
| 367 | using namespace NFS2_DATA; |
| 368 | |
| 369 | LOG(INFO) << "Extracting PSH file: " << psh_path << " to " << output_path; |
| 370 | |
| 371 | if (boost::filesystem::exists(output_path)) { |
| 372 | LOG(INFO) << "Textures already exist at " << output_path << ". Nothing to extract."; |
| 373 | return true; |
| 374 | } |
| 375 | |
| 376 | boost::filesystem::create_directories(output_path); |
| 377 | ifstream psh(psh_path, ios::in | ios::binary); |
| 378 | |
| 379 | PS1::PSH::HEADER *pshHeader = new PS1::PSH::HEADER(); |
| 380 | |
| 381 | // Check we're in a valid TRK file |
| 382 | if (psh.read(((char *) pshHeader), sizeof(PS1::PSH::HEADER)).gcount() != sizeof(PS1::PSH::HEADER)) { |
| 383 | LOG(WARNING) << "Couldn't open file/truncated."; |
| 384 | delete pshHeader; |
| 385 | return false; |
| 386 | } |
| 387 | |
| 388 | LOG(INFO) << pshHeader->nDirectories << " images inside PSH"; |
| 389 | |
| 390 | // Header should contain TRAC |
| 391 | if (memcmp(pshHeader->header, "SHPP", sizeof(pshHeader->header)) != 0 && |
| 392 | memcmp(pshHeader->chk, "GIMX", sizeof(pshHeader->chk)) != 0) { |
| 393 | LOG(WARNING) << "Invalid PSH Header(s)."; |
| 394 | delete pshHeader; |
| 395 | return false; |
| 396 | } |
| 397 | |
| 398 | // Get the offsets to each image in the PSH |
| 399 | auto *directoryEntries = new PS1::PSH::DIR_ENTRY[pshHeader->nDirectories]; |
| 400 | psh.read(((char *) directoryEntries), pshHeader->nDirectories * sizeof(PS1::PSH::DIR_ENTRY)); |
| 401 | |
| 402 | for (uint32_t image_Idx = 0; image_Idx < pshHeader->nDirectories; ++image_Idx) { |
| 403 | LOG(INFO) << "Extracting GIMX " << image_Idx << ": " << directoryEntries[image_Idx].imageName[0] << directoryEntries[image_Idx].imageName[1] << directoryEntries[image_Idx].imageName[2] << directoryEntries[image_Idx].imageName[3] << ".BMP"; |
| 404 | psh.seekg(directoryEntries[image_Idx].imageOffset, ios_base::beg); |
| 405 | auto *imageHeader = new PS1::PSH::IMAGE_HEADER(); |
| 406 | psh.read(((char *) imageHeader), sizeof(PS1::PSH::IMAGE_HEADER)); |
| 407 | |
| 408 | uint8_t bitDepth = static_cast<uint8_t>(imageHeader->imageType & 0x3); |
| 409 | uint32_t *pixels = new uint32_t[imageHeader->width * imageHeader->height]; |
| 410 | uint8_t *indexPair = new uint8_t(); |
| 411 | uint8_t *indexes = new uint8_t[imageHeader->width * imageHeader->height]; // Only used if indexed |
| 412 | bool hasAlpha = false; |
| 413 | bool isPadded = false; |
| 414 | if (bitDepth == 0) { |
| 415 | isPadded = (imageHeader->width % 4 == 1) || (imageHeader->width % 4 == 2); |
| 416 | } else if (bitDepth == 1 || bitDepth == 3) { |
| 417 | isPadded = imageHeader->width % 2 == 1; |
| 418 | } |
| 419 | |
| 420 | for (int y = 0; y < imageHeader->height; y++) { |
| 421 | for (int x = 0; x < imageHeader->width; x++) { |
| 422 | switch (bitDepth) { |
| 423 | case 0: { // 4-bit indexed colour |
no test coverage detected