From 3fdfc087503b148edcebb9e6f508f0fdb3fa5bf1 Mon Sep 17 00:00:00 2001 From: lzwdgc Date: Tue, 10 Jan 2023 02:42:16 +0300 Subject: [PATCH] Initial m2 unpacker (unpaker2). --- src/unpaker2/unpaker2.cpp | 167 ++++++++++++++++++++++++++++++++++++++ sw.cpp | 11 ++- 2 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 src/unpaker2/unpaker2.cpp diff --git a/src/unpaker2/unpaker2.cpp b/src/unpaker2/unpaker2.cpp new file mode 100644 index 0000000..8bfc138 --- /dev/null +++ b/src/unpaker2/unpaker2.cpp @@ -0,0 +1,167 @@ +/* + * AIM mod_converter + * Copyright (C) 2015 lzwdgc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +//#include +#include +#include + +using namespace std; + +constexpr auto supported_block_size = 32768; + +#pragma pack(push, 1) +struct pak { + uint32_t magic; + uint16_t unk0; + uint32_t n_files; + uint32_t n_blocks; + uint32_t block_size; + uint32_t unk1; +}; +struct file_description { + const char name[0x50]; + uint32_t offset; + uint32_t size; +}; +struct segment { + enum decode_algorithm : uint32_t { + none = 0x0, + lzo = 0x1, + lzma = 0x2, + rlew = 0x4, + }; + + uint32_t unk1; // some file offset? trash? + decode_algorithm algorithm; + uint32_t offset; +}; +#pragma pack(pop) + +struct stream { + primitives::templates2::mmap_file &m; + uint8_t *p{m.p}; + + template + operator T&() { + auto &r = *(T*)p; + p += sizeof(T); + return r; + } + template + auto span(size_t len) { + auto s = std::span((T*)p, len); + p += sizeof(T) * len; + return s; + } +}; + +struct decoded_block { + uint8_t out[supported_block_size]; +}; + +void unpack_file(path fn) { + primitives::templates2::mmap_file f{fn}; + stream s{f}; + pak p = s; + if (p.block_size != supported_block_size) { + throw std::runtime_error{"block size mismatch"}; + } + auto descs = s.span(p.n_files); + auto segments = s.span(p.n_blocks); + std::vector bbb; + bbb.resize(segments.size() * supported_block_size); + auto pp = bbb.data(); + std::vector dblocks; + for (auto &&seg : segments) { + s.p = f.p + seg.offset; + auto &b = dblocks.emplace_back(); + uint32_t len = s; + switch (seg.algorithm) { + case segment::decode_algorithm::none: { + //memcpy(b.out, s.p, len); + memcpy(pp, s.p, len); + break; + } + case segment::decode_algorithm::lzo: { + size_t outsz = supported_block_size; + //auto r2 = lzo1x_decompress(s.p, len, b.out, &outsz, 0); + auto r2 = lzo1x_decompress(s.p, len, pp, &outsz, 0); + if (r2 != LZO_E_OK) { + throw std::runtime_error{"lzo error"}; + } + break; + } + default: + throw std::runtime_error{"compression unsupported"}; + } + pp += len; + } + pp = bbb.data(); + + /*uint8_t out[32768]; + uint64_t memlimit = 0; + size_t in_pos = 0; + size_t out_pos = 0; + auto r = lzma_stream_buffer_decode(&memlimit, 0, 0, s.p, &in_pos, f.p+f.sz-s.p, out, &out_pos, 1'000'000'000);*/ + + auto dir = fn += ".dir2"; + fs::create_directories(dir); + for (auto &&d : descs) { + auto fn = dir / d.name; + fs::create_directories(fn.parent_path()); + std::ofstream o{fn, std::ios::binary}; + o.write((const char *)pp + d.offset, d.size); + } +} + +int main(int argc, char *argv[]) { + cl::opt p(cl::Positional, cl::desc(""), cl::Required); + + cl::ParseCommandLineOptions(argc, argv); + + if (fs::is_regular_file(p)) { + unpack_file(p); + } else if (fs::is_directory(p)) { + auto files = enumerate_files_like(p, ".*\\.pak", false); + for (auto &f : files) { + if (f.has_extension()) + continue; + std::cout << "processing: " << f << "\n"; + unpack_file(f); + } + } else { + throw std::runtime_error("Bad fs object"); + } + return 0; +} diff --git a/sw.cpp b/sw.cpp index 0f9474e..cd4e757 100644 --- a/sw.cpp +++ b/sw.cpp @@ -11,13 +11,19 @@ void build(Solution &s) common += cppstd; common.setRootDirectory("src/common"); common.Public += "pub.egorpugin.primitives.filesystem"_dep; + common.Public += "pub.egorpugin.primitives.templates2"_dep; - auto add_exe = [&](const String &name) -> decltype(auto) + auto add_exe_base = [&](const String &name) -> decltype(auto) { auto &t = tools.addExecutable(name); t.PackageDefinitions = true; t += cppstd; t.setRootDirectory("src/" + name); + return t; + }; + auto add_exe = [&](const String &name) -> decltype(auto) + { + auto &t = add_exe_base(name); t += "pub.egorpugin.primitives.sw.main"_dep; return t; }; @@ -45,9 +51,10 @@ void build(Solution &s) add_exe_with_common("tm_converter"); add_exe("name_generator"); add_exe_with_common("save_loader"); - auto &unpaker = add_exe("unpaker"); // 32-bit only + auto &unpaker = add_exe_base("unpaker"); // 32-bit only if (unpaker.getBuildSettings().TargetOS.Arch != ArchType::x86) unpaker.HeaderOnly = true; + add_exe_with_common("unpaker2") += "org.sw.demo.libarchive.libarchive"_dep; // not so simple targets auto &script2txt = add_exe_with_common("script2txt");