From 60a490d5c3576ccc79c2a3bb820acf718818f243 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Tue, 12 Apr 2022 20:15:30 -0400 Subject: [PATCH] support unpacking iso files in the extractor (#1300) --- common/CMakeLists.txt | 1 + common/util/read_iso_file.cpp | 114 ++++++++++++++++++++++++++++++++++ common/util/read_iso_file.h | 30 +++++++++ decompiler/extractor/main.cpp | 12 +++- 4 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 common/util/read_iso_file.cpp create mode 100644 common/util/read_iso_file.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index eb9bb73ceb..05f6127624 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -34,6 +34,7 @@ add_library(common util/diff.cpp util/FileUtil.cpp util/json_util.cpp + util/read_iso_file.cpp util/Timer.cpp util/os.cpp util/print_float.cpp diff --git a/common/util/read_iso_file.cpp b/common/util/read_iso_file.cpp new file mode 100644 index 0000000000..837ee5f82a --- /dev/null +++ b/common/util/read_iso_file.cpp @@ -0,0 +1,114 @@ +#include "read_iso_file.h" +#include "third-party/fmt/core.h" +#include "common/common_types.h" +#include "common/util/Assert.h" +#include "common/util/FileUtil.h" + +IsoFile::IsoFile() { + root.is_dir = true; +} + +std::string IsoFile::print() const { + std::string result; + root.print(&result, ""); + return result; +} + +void IsoFile::Entry::print(std::string* result, const std::string& prefix) const { + if (is_dir) { + std::string child_prefix = prefix + "/" + name; + for (const auto& child : children) { + child.print(result, child_prefix); + } + } else { + result->append(prefix); + result->push_back('/'); + result->append(name); + result->push_back('\n'); + } +} + +namespace { +constexpr int SECTOR_SIZE = 0x800; + +template +T read_file(FILE* fp, u32 sector, u32 offset_in_sector) { + T result; + if (fseek(fp, sector * SECTOR_SIZE + offset_in_sector, SEEK_SET)) { + ASSERT_MSG(false, "Failed to fseek iso"); + } + if (fread(&result, sizeof(T), 1, fp) != 1) { + ASSERT_MSG(false, "Failed to fread iso"); + } + return result; +} + +void add_from_dir(FILE* fp, u32 sector, u32 size, IsoFile::Entry* parent) { + u32 offset = 0; + while (offset < size) { + if (!read_file(fp, sector, offset)) { + offset = (offset & ~(SECTOR_SIZE - 1)) + SECTOR_SIZE; + continue; + } + u8 record_size = read_file(fp, sector, offset); + u8 kind = read_file(fp, sector, offset + 0x21); + if ((kind != 0) && (kind != 1)) { + auto& entry = parent->children.emplace_back(); + u32 extent = read_file(fp, sector, offset + 2); + u32 dir_or_file_size = read_file(fp, sector, offset + 10); + u32 name_len = read_file(fp, sector, offset + 32); + u8 c0 = read_file(fp, sector, offset + name_len + 0x1f); + u8 c1 = read_file(fp, sector, offset + name_len + 0x20); + for (u32 i = 0; i < name_len; i++) { + entry.name.push_back(read_file(fp, sector, offset + 0x21 + i)); + } + entry.is_dir = (c0 != ';' || c1 != '1'); + if (entry.is_dir) { + add_from_dir(fp, extent, dir_or_file_size, &entry); + } else { + entry.name.pop_back(); + entry.name.pop_back(); + entry.offset_in_file = SECTOR_SIZE * extent; + entry.size = dir_or_file_size; + } + } + offset += record_size; + } +} + +void unpack_entry(FILE* fp, const IsoFile::Entry& entry, const std::filesystem::path& dest) { + std::filesystem::path path_to_entry = dest / entry.name; + if (entry.is_dir) { + std::filesystem::create_directory(path_to_entry); + for (const auto& child : entry.children) { + unpack_entry(fp, child, path_to_entry); + } + } else { + std::vector buffer(entry.size); + if (fseek(fp, entry.offset_in_file, SEEK_SET)) { + ASSERT_MSG(false, "Failed to fseek iso when unpacking"); + } + if (fread(buffer.data(), buffer.size(), 1, fp) != 1) { + ASSERT_MSG(false, "Failed to fread iso when unpacking"); + } + file_util::write_binary_file(path_to_entry.string(), buffer.data(), buffer.size()); + } +} +} // namespace + +IsoFile find_files_in_iso(FILE* fp) { + IsoFile result; + u32 path_table_sector = read_file(fp, 0x10, 0x8c); + u32 path_table_extent = read_file(fp, path_table_sector, 2); + u32 dir_size = read_file(fp, path_table_extent, 10); + add_from_dir(fp, path_table_extent, dir_size, &result.root); + return result; +} + +void unpack_iso_files(FILE* fp, const IsoFile& layout, const std::filesystem::path& dest) { + unpack_entry(fp, layout.root, dest); +} + +void unpack_iso_files(FILE* fp, const std::filesystem::path& dest) { + unpack_iso_files(fp, find_files_in_iso(fp), dest); +} diff --git a/common/util/read_iso_file.h b/common/util/read_iso_file.h new file mode 100644 index 0000000000..af771db8f9 --- /dev/null +++ b/common/util/read_iso_file.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +struct IsoFile { + struct Entry { + bool is_dir = false; + std::string name; + + // if file + size_t offset_in_file = 0; + size_t size = 0; + + // if dir + std::vector children; + void print(std::string* result, const std::string& prefix) const; + }; + + std::string print() const; + + Entry root; + + IsoFile(); +}; + +IsoFile find_files_in_iso(FILE* fp); +void unpack_iso_files(FILE* fp, const IsoFile& layout, const std::filesystem::path& dest); +void unpack_iso_files(FILE* fp, const std::filesystem::path& dest); \ No newline at end of file diff --git a/decompiler/extractor/main.cpp b/decompiler/extractor/main.cpp index a06ede7e32..43bd52e80e 100644 --- a/decompiler/extractor/main.cpp +++ b/decompiler/extractor/main.cpp @@ -5,6 +5,7 @@ #include "decompiler/level_extractor/extract_level.h" #include "decompiler/config.h" #include "goalc/compiler/Compiler.h" +#include "common/util/read_iso_file.h" void setup_global_decompiler_stuff() { file_util::init_crc(); @@ -31,8 +32,15 @@ int main(int argc, char** argv) { } if (!std::filesystem::is_directory(jak1_input_files)) { - fmt::print("Error: input folder {} is not a folder.\n", jak1_input_files.string()); - return 1; + fmt::print("Note: input isn't a folder, assuming it's an ISO file...\n"); + auto path_to_iso_files = file_util::get_jak_project_dir() / "extracted_iso"; + std::filesystem::create_directories(path_to_iso_files); + + auto fp = fopen(jak1_input_files.string().c_str(), "rb"); + ASSERT_MSG(fp, "failed to open input ISO file\n"); + unpack_iso_files(fp, path_to_iso_files); + fclose(fp); + jak1_input_files = path_to_iso_files; } if (!std::filesystem::exists(jak1_input_files / "DGO")) {