diff --git a/toolset/CMakeLists.txt b/toolset/CMakeLists.txt index 9165082..d340225 100644 --- a/toolset/CMakeLists.txt +++ b/toolset/CMakeLists.txt @@ -35,6 +35,7 @@ add_spike_subdir(obb_extract) add_spike_subdir(lmt_to_gltf) add_spike_subdir(sdl_conv) add_spike_subdir(dlc_extract) +add_spike_subdir(fpk_extract) if(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Release") add_spike_subdir(dev) diff --git a/toolset/fpk_extract/CMakeLists.txt b/toolset/fpk_extract/CMakeLists.txt new file mode 100644 index 0000000..225764f --- /dev/null +++ b/toolset/fpk_extract/CMakeLists.txt @@ -0,0 +1,19 @@ +project(FPKExtract) + +build_target( + NAME + fpk_extract + TYPE + ESMODULE + VERSION + 1 + SOURCES + fpk_extract.cpp + LINKS + spike-interface + AUTHOR + "Lukas Cone" + DESCR + "FPK Archive Extractor" + START_YEAR + 2024) diff --git a/toolset/fpk_extract/fpk_extract.cpp b/toolset/fpk_extract/fpk_extract.cpp new file mode 100644 index 0000000..a5f7ff2 --- /dev/null +++ b/toolset/fpk_extract/fpk_extract.cpp @@ -0,0 +1,98 @@ +/* FPKExtract + Copyright(C) 2024 Lukas Cone + + 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 "project.h" +#include "spike/app_context.hpp" +#include "spike/except.hpp" +#include "spike/io/binreader_stream.hpp" + +std::string_view filters[]{ + ".fpk$", +}; + +static AppInfo_s appInfo{ + .filteredLoad = true, + .header = FPKExtract_DESC " v" FPKExtract_VERSION ", " FPKExtract_COPYRIGHT + "Lukas Cone", + .filters = filters, +}; + +AppInfo_s *AppInitModule() { return &appInfo; } + +static const uint32 ID = CompileFourCC("FPK"); + +struct FPKHeader { + uint32 id; + uint32 null[2]; + uint16 version; + uint16 numFiles; +}; + +struct FPKFile { + char path[0x40]; + uint32 unk; + uint32 size0; + uint32 size1; + uint32 offset; +}; + +void AppProcessFile(AppContext *ctx) { + BinReaderRef rd(ctx->GetStream()); + FPKHeader hdr; + rd.Read(hdr); + + if (hdr.id != ID) { + throw es::InvalidHeaderError(hdr.id); + } + + if (hdr.version != 2) { + throw es::InvalidVersionError(hdr.version); + } + + std::vector files; + rd.ReadContainer(files, hdr.numFiles); + + auto ectx = ctx->ExtractContext(); + + if (ectx->RequiresFolders()) { + for (auto &f : files) { + const char *fileName = f.path; + while (*fileName == '/') { + fileName++; + } + + AFileInfo finf(fileName); + + ectx->AddFolderPath(std::string(finf.GetFolder())); + } + + ectx->GenerateFolders(); + } + + std::string buffer; + + for (auto &f : files) { + rd.Seek(f.offset); + rd.ReadContainer(buffer, f.size0); + const char *fileName = f.path; + while (*fileName == '/') { + fileName++; + } + ectx->NewFile(fileName); + ectx->SendData(buffer); + } +}