From 570e9900fd7b8d15c987c7123e324c33a16822e0 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Wed, 16 Mar 2022 18:02:39 -0400 Subject: [PATCH] Add installer extract functionality --- CMakeLists.txt | 11 +++- include/installextract.h | 5 ++ src/installextract.cpp | 115 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 include/installextract.h create mode 100644 src/installextract.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e599ca1..9386b43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,7 @@ project(libxiv) +find_package(fmt) + add_library(libxiv STATIC src/fiinparser.cpp src/indexparser.cpp @@ -7,5 +9,10 @@ add_library(libxiv STATIC src/gamedata.cpp src/compression.cpp src/exhparser.cpp - src/exdparser.cpp) -target_include_directories(libxiv PUBLIC include PRIVATE src) \ No newline at end of file + src/exdparser.cpp + src/installextract.cpp) +target_include_directories(libxiv PUBLIC include PRIVATE src) +target_link_libraries(libxiv PUBLIC + unshield + fmt::fmt + z) \ No newline at end of file diff --git a/include/installextract.h b/include/installextract.h new file mode 100644 index 0000000..3264e92 --- /dev/null +++ b/include/installextract.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +void extractBootstrapFiles(const std::string_view installer); \ No newline at end of file diff --git a/src/installextract.cpp b/src/installextract.cpp new file mode 100644 index 0000000..0d27b49 --- /dev/null +++ b/src/installextract.cpp @@ -0,0 +1,115 @@ +#include "installextract.h" + +#include +#include +#include +#include + +const std::array filesToExtract = {"data1.cab", "data1.hdr", "data2.cab"}; + +const std::array bootComponent = { + "cef_license.txt", + "FFXIV.ico", + "ffxivboot.exe", + "ffxivboot.ver", + "ffxivboot64.exe", + "ffxivconfig.exe", + "ffxivconfig64.exe", + "ffxivlauncher.exe", + "ffxivlauncher64.exe", + "ffxivsysinfo.exe", + "ffxivsysinfo64.exe", + "ffxivupdater.exe", + "ffxivupdater64.exe", + "FFXIV_sysinfo.ico", + "icudt.dll", + "libcef.dll", + "license.txt", + "locales/reserved.txt" +}; + +const std::array gameComponent = { + "ffxivgame.ver" +}; + +void extractBootstrapFiles(const std::string_view installer) { + FILE* file = fopen(installer.data(), "rb"); + if(file == nullptr) { + throw std::runtime_error("Failed to open installer {}" + std::string(installer)); + } + + fseek(file, 0, SEEK_END); + size_t fileSize = ftell(file); + rewind(file); + + auto buffer = static_cast(malloc(sizeof(char) * fileSize)); + + fread(buffer, 1, fileSize, file); + + // some really, really bad code to basically read the cab files which are put right next to each other in the exe file + char* lastNeedle = nullptr; + std::string lastFilename; + for(auto fileName : filesToExtract) { + // all files are located in Disk1 + const std::string realFileName = fmt::format("Disk1\\{}", fileName); + const char* needle = realFileName.c_str(); + + // make unshield shut up + unshield_set_log_level(0); + + // TODO: is this while really needed? it should only find it once + while(true) { + char* p = static_cast(memmem(buffer, fileSize, needle, realFileName.length())); + if(!p) + break; + + if(lastNeedle != nullptr) { + FILE* newFile = fopen(lastFilename.data(), "wb"); + + if(lastFilename == "data1.hdr") { + fwrite(lastNeedle + 30, p - lastNeedle - 42, 1, newFile); + } else { + fwrite(lastNeedle + 32, p - lastNeedle - 42, 1, newFile); + } + + // actual cab files start 32 bytes and we need to peel 42 bytes off of the end + fclose(newFile); + } + + lastFilename = fileName; + lastNeedle = p; + fileSize -= (p + 4) - buffer; + } + + // write final file + FILE* newFile = fopen(lastFilename.data(), "wb"); + fmt::print("Wrote {}!\n", lastFilename); + fwrite(lastNeedle + 33, fileSize - 42, 1, newFile); + + // actual cab files start 32 bytes and we need to peel 42 bytes off of the end + fclose(newFile); + } + + auto unshield = unshield_open("data1.cab"); + const int fileCount = unshield_file_count(unshield); + + for(int i = 0 ; i < fileCount; i++) { + const char* filename = unshield_file_name(unshield, i); + + for(auto bootName : bootComponent) { + fmt::print("Extracted {}\n", bootName); + + if(strcmp(filename, bootName) == 0) + unshield_file_save(unshield, i, bootName); + } + + for(auto gameName : gameComponent) { + fmt::print("Extracted {}\n", gameName); + + if(strcmp(filename, gameName) == 0) + unshield_file_save(unshield, i, gameName); + } + } + + fclose(file); +} \ No newline at end of file