Archived
1
Fork 0

Add installer extract functionality

This commit is contained in:
Joshua Goins 2022-03-16 18:02:39 -04:00
parent ddd3993db8
commit 570e9900fd
3 changed files with 129 additions and 2 deletions

View file

@ -1,5 +1,7 @@
project(libxiv) project(libxiv)
find_package(fmt)
add_library(libxiv STATIC add_library(libxiv STATIC
src/fiinparser.cpp src/fiinparser.cpp
src/indexparser.cpp src/indexparser.cpp
@ -7,5 +9,10 @@ add_library(libxiv STATIC
src/gamedata.cpp src/gamedata.cpp
src/compression.cpp src/compression.cpp
src/exhparser.cpp src/exhparser.cpp
src/exdparser.cpp) src/exdparser.cpp
target_include_directories(libxiv PUBLIC include PRIVATE src) src/installextract.cpp)
target_include_directories(libxiv PUBLIC include PRIVATE src)
target_link_libraries(libxiv PUBLIC
unshield
fmt::fmt
z)

5
include/installextract.h Normal file
View file

@ -0,0 +1,5 @@
#pragma once
#include <string_view>
void extractBootstrapFiles(const std::string_view installer);

115
src/installextract.cpp Normal file
View file

@ -0,0 +1,115 @@
#include "installextract.h"
#include <cstdio>
#include <stdexcept>
#include <libunshield.h>
#include <fmt/format.h>
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<char*>(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<char*>(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);
}