Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions .idea/runConfigurations/Copy_x86_debug_to_Far.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions .idea/runConfigurations/Copy_x86_release_to_Far.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 0 additions & 11 deletions .idea/runConfigurations/Far_Manager__Debug_.xml

This file was deleted.

11 changes: 0 additions & 11 deletions .idea/runConfigurations/Far_Manager__Release_.xml

This file was deleted.

11 changes: 11 additions & 0 deletions .idea/runConfigurations/Far_x64_debug.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions .idea/runConfigurations/Far_x64_release.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions .idea/runConfigurations/Far_x86_debug.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions .idea/runConfigurations/Far_x86_release.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ find_package(ZLIB REQUIRED)
find_path(ZSTR_INCLUDE_DIRS "zstr.hpp")

set(ALL_MODULES renpy rpgmaker zanzarah)
set(RELEASED_MODULES renpy zanzarah)
set(RELEASED_MODULES renpy rpgmaker zanzarah)

set(RENPY_DIR src/modules/renpy)
add_library(renpy SHARED
Expand Down Expand Up @@ -55,6 +55,7 @@ add_executable(tests
src/tests/framework/observer.cpp
src/tests/framework/testcase.cpp
src/tests/renpy.cpp
src/tests/rpgmaker.cpp
src/tests/zanzarah.cpp
)
target_link_libraries(tests PRIVATE Catch2::Catch2WithMain)
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@ specific files as needed without having to unpack the entire archive.
- Compatibility: RPA-2.0, RPA-3.0
- Description: Archives used in Ren'Py visual novels to store game resources (graphics, sound, scripts)

### RPG Maker

- Supported extensions: `.rgss3a`
- Compatibility: RPG Maker VX Ace (RGSS3)
- Description: Encrypted archives containing game assets like graphics, audio, and data files

### Zanzarah: The Hidden Portal

- Supported extension: `.pak`
- Supported extensions: `.pak`
- Compatibility: Supports archives from both the original game version and the Steam version
- Description: Archive used to store all game resources

Expand Down
2 changes: 1 addition & 1 deletion copy_dlls.cmd
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@echo off
pushd %~dp0\build\%1
pushd %~dp0build\%1
set MODULES_DIR=%DEBUGFARHOME%\Plugins\Observer\modules
copy /Y *.so %MODULES_DIR% || exit 1
copy /Y *.pdb %MODULES_DIR%
Expand Down
6 changes: 5 additions & 1 deletion src/archive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ namespace archive
throw read_error();
}

uint32_t magic = file.magic;
int64_t bytes_left = file.compressed_body_size_in_bytes;
while (bytes_left > 0) {
const auto chunk_size = static_cast<std::streamsize>(std::min(bytes_left, buffer_size));
Expand All @@ -96,8 +97,11 @@ namespace archive
throw read_error();
}

buffer.resize(static_cast<size_t>(chunk_size));
magic = extractor_->decrypt(magic, buffer);

try {
output.write(buffer.data(), chunk_size);
output.write(buffer.data(), buffer.size());
} catch (std::ios_base::failure &) {
throw write_error();
}
Expand Down
5 changes: 5 additions & 0 deletions src/modules/extractor.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,20 @@ namespace extractor
int64_t offset;
int64_t compressed_body_size_in_bytes;
int64_t uncompressed_body_size_in_bytes;
uint32_t magic = 0;
};

class extractor
{
public:
virtual ~extractor() = default;

static std::vector<std::byte> get_signature() noexcept;

archive_info get_archive_info(const std::span<const std::byte> &data) noexcept;

std::vector<std::unique_ptr<file> > list_files(std::ifstream &stream);

uint32_t decrypt(uint32_t magic, std::vector<char> &data) const;
};
}
5 changes: 5 additions & 0 deletions src/modules/renpy/renpy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,9 @@ namespace extractor
}
return files;
}

uint32_t extractor::decrypt(uint32_t magic, std::vector<char> &data) const
{
return magic;
}
}
38 changes: 32 additions & 6 deletions src/modules/rpgmaker/rpgmaker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,7 @@ namespace extractor
// ReSharper disable once CppMemberFunctionMayBeStatic
archive_info extractor::get_archive_info(const std::span<const std::byte> &data) noexcept // NOLINT(*-convert-member-functions-to-static)
{
// TODO implement: RGSS => RPG Maker XP, 0xDEADCAFE, XOR => zlib
// TODO implement: RGSS2 => RPG Maker VX, 0xCAFEDEAD, XOR => zlib
// TODO RPG Maker MV
// TODO RPG Maker MZ
// TODO .rvdata, .rvdata2
return archive_info{L"RGSS3", L"DEFLATE", L"RPG Maker VX Ace"};
return archive_info{L"RGSS3", L"-", L"RPG Maker VX Ace"};
}

static uint32_t read_u32(std::ifstream &stream)
Expand Down Expand Up @@ -68,8 +63,39 @@ namespace extractor
new_file->offset = offset;
new_file->compressed_body_size_in_bytes = size;
new_file->uncompressed_body_size_in_bytes = size;
new_file->magic = file_magic;
files.push_back(std::move(new_file));
}
return files;
}

static uint32_t advance_magic(uint32_t &magic)
{
const uint32_t old = magic;
magic = magic * 7 + 3;
return old;
}

// ReSharper disable once CppMemberFunctionMayBeStatic
uint32_t extractor::decrypt(uint32_t magic, std::vector<char> &data) const // NOLINT(*-convert-member-functions-to-static)
{
const size_t size = data.size();
size_t i = 0;

while (i + 4 <= size) {
uint32_t value;
std::memcpy(&value, &data[i], sizeof(value));
value ^= advance_magic(magic);
std::memcpy(&data[i], &value, sizeof(value));
i += 4;
}

while (i < size) {
const auto byte_value = static_cast<uint8_t>(magic >> (i % 4 * 8));
data[i] = static_cast<char>(static_cast<uint8_t>(data[i]) ^ byte_value);
++i;
}

return magic;
}
}
5 changes: 5 additions & 0 deletions src/modules/zanzarah/zanzarah.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,9 @@ namespace extractor

return files;
}

uint32_t extractor::decrypt(uint32_t magic, std::vector<char> &data) const
{
return magic;
}
}
2 changes: 1 addition & 1 deletion src/tests/framework/observer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#include "../../api.h"

#include <fstream>
#include <iostream>

#include <windows.h>
#include <catch2/catch_test_macros.hpp>
Expand Down Expand Up @@ -143,6 +142,7 @@ namespace test
observer::observer()
{
modules_.push_back(std::make_unique<c_module>("renpy.so"));
modules_.push_back(std::make_unique<c_module>("rpgmaker.so"));
modules_.push_back(std::make_unique<c_module>("zanzarah.so"));
}

Expand Down
50 changes: 50 additions & 0 deletions src/tests/rpgmaker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include <catch2/catch_test_macros.hpp>

#include "framework/testcase.h"

using namespace test;

TEST_CASE("rpgmaker: GirlsXLust_v1.0")
{
test_on("rpgmaker\\GirlsXLust_v1.0.rgss3a");
}

TEST_CASE("rpgmaker: Maid-X-Demon-Mari-s-First-Job")
{
test_on("rpgmaker\\Maid-X-Demon-Mari-s-First-Job.rgss3a");
}

TEST_CASE("rpgmaker: MaiDensnow_Eve")
{
test_on("rpgmaker\\MaiDensnow_Eve.rgss3a");
}

TEST_CASE("rpgmaker: MaidsPerfect_v1.0a")
{
test_on("rpgmaker\\MaidsPerfect_v1.0a.rgss3a");
}

TEST_CASE("rpgmaker: NotSoOrdinaryStory")
{
test_on("rpgmaker\\NotSoOrdinaryStory.rgss3a");
}

TEST_CASE("rpgmaker: A_Song_of_Elfpai_and_Tentacles_1.20_ENGLISH_")
{
test_on("rpgmaker\\RJ135050_-_A_Song_of_Elfpai_and_Tentacles_1.20_ENGLISH_.rgss3a");
}

TEST_CASE("rpgmaker: ThroughTheStaticAlpha_v0.1")
{
test_on("rpgmaker\\ThroughTheStaticAlpha_v0.1.rgss3a");
}

TEST_CASE("rpgmaker: WarlockAndBoobs_v0.350.1.4")
{
test_on("rpgmaker\\WarlockAndBoobs_v0.350.1.4.rgss3a");
}

TEST_CASE("rpgmaker: WarlockAndBoobs_v0.422.0.1")
{
test_on("rpgmaker\\WarlockAndBoobs_v0.422.0.1.rgss3a");
}