diff --git a/.idea/runConfigurations/Copy_Debug_DLLs_to_Debug_Version_of_Far_Manager.xml b/.idea/runConfigurations/Copy_x64_debug_to_Far.xml
similarity index 59%
rename from .idea/runConfigurations/Copy_Debug_DLLs_to_Debug_Version_of_Far_Manager.xml
rename to .idea/runConfigurations/Copy_x64_debug_to_Far.xml
index bce80b7..e0a01cc 100644
--- a/.idea/runConfigurations/Copy_Debug_DLLs_to_Debug_Version_of_Far_Manager.xml
+++ b/.idea/runConfigurations/Copy_x64_debug_to_Far.xml
@@ -1,14 +1,14 @@
-
+
-
+
-
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Copy_Release_DLLs_to_Debug_Version_of_Far_Manager.xml b/.idea/runConfigurations/Copy_x64_release_to_Far.xml
similarity index 59%
rename from .idea/runConfigurations/Copy_Release_DLLs_to_Debug_Version_of_Far_Manager.xml
rename to .idea/runConfigurations/Copy_x64_release_to_Far.xml
index b2500a3..0c2d86e 100644
--- a/.idea/runConfigurations/Copy_Release_DLLs_to_Debug_Version_of_Far_Manager.xml
+++ b/.idea/runConfigurations/Copy_x64_release_to_Far.xml
@@ -1,14 +1,14 @@
-
+
-
+
-
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Copy_x86_debug_to_Far.xml b/.idea/runConfigurations/Copy_x86_debug_to_Far.xml
new file mode 100644
index 0000000..2096b9b
--- /dev/null
+++ b/.idea/runConfigurations/Copy_x86_debug_to_Far.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Copy_x86_release_to_Far.xml b/.idea/runConfigurations/Copy_x86_release_to_Far.xml
new file mode 100644
index 0000000..808b0e3
--- /dev/null
+++ b/.idea/runConfigurations/Copy_x86_release_to_Far.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Far_Manager__Debug_.xml b/.idea/runConfigurations/Far_Manager__Debug_.xml
deleted file mode 100644
index 3fe8131..0000000
--- a/.idea/runConfigurations/Far_Manager__Debug_.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/Far_Manager__Release_.xml b/.idea/runConfigurations/Far_Manager__Release_.xml
deleted file mode 100644
index 3da7b08..0000000
--- a/.idea/runConfigurations/Far_Manager__Release_.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations/Far_x64_debug.xml b/.idea/runConfigurations/Far_x64_debug.xml
new file mode 100644
index 0000000..bed2e21
--- /dev/null
+++ b/.idea/runConfigurations/Far_x64_debug.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Far_x64_release.xml b/.idea/runConfigurations/Far_x64_release.xml
new file mode 100644
index 0000000..3fe2c78
--- /dev/null
+++ b/.idea/runConfigurations/Far_x64_release.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Far_x86_debug.xml b/.idea/runConfigurations/Far_x86_debug.xml
new file mode 100644
index 0000000..f1492bb
--- /dev/null
+++ b/.idea/runConfigurations/Far_x86_debug.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Far_x86_release.xml b/.idea/runConfigurations/Far_x86_release.xml
new file mode 100644
index 0000000..3dd962a
--- /dev/null
+++ b/.idea/runConfigurations/Far_x86_release.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f387421..6508d49 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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
@@ -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)
diff --git a/README.md b/README.md
index 73916ce..5063a2e 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/copy_dlls.cmd b/copy_dlls.cmd
index d5d52c2..e12a55d 100644
--- a/copy_dlls.cmd
+++ b/copy_dlls.cmd
@@ -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%
diff --git a/src/archive.cpp b/src/archive.cpp
index aae595c..e284497 100644
--- a/src/archive.cpp
+++ b/src/archive.cpp
@@ -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::min(bytes_left, buffer_size));
@@ -96,8 +97,11 @@ namespace archive
throw read_error();
}
+ buffer.resize(static_cast(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();
}
diff --git a/src/modules/extractor.h b/src/modules/extractor.h
index 86da9a5..5b5e566 100644
--- a/src/modules/extractor.h
+++ b/src/modules/extractor.h
@@ -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 get_signature() noexcept;
archive_info get_archive_info(const std::span &data) noexcept;
std::vector > list_files(std::ifstream &stream);
+
+ uint32_t decrypt(uint32_t magic, std::vector &data) const;
};
}
diff --git a/src/modules/renpy/renpy.cpp b/src/modules/renpy/renpy.cpp
index d3c9399..63b047b 100644
--- a/src/modules/renpy/renpy.cpp
+++ b/src/modules/renpy/renpy.cpp
@@ -127,4 +127,9 @@ namespace extractor
}
return files;
}
+
+ uint32_t extractor::decrypt(uint32_t magic, std::vector &data) const
+ {
+ return magic;
+ }
}
diff --git a/src/modules/rpgmaker/rpgmaker.cpp b/src/modules/rpgmaker/rpgmaker.cpp
index 65c55c0..af6baf0 100644
--- a/src/modules/rpgmaker/rpgmaker.cpp
+++ b/src/modules/rpgmaker/rpgmaker.cpp
@@ -25,12 +25,7 @@ namespace extractor
// ReSharper disable once CppMemberFunctionMayBeStatic
archive_info extractor::get_archive_info(const std::span &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)
@@ -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 &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(magic >> (i % 4 * 8));
+ data[i] = static_cast(static_cast(data[i]) ^ byte_value);
+ ++i;
+ }
+
+ return magic;
+ }
}
diff --git a/src/modules/zanzarah/zanzarah.cpp b/src/modules/zanzarah/zanzarah.cpp
index ae619be..e4cb88f 100644
--- a/src/modules/zanzarah/zanzarah.cpp
+++ b/src/modules/zanzarah/zanzarah.cpp
@@ -87,4 +87,9 @@ namespace extractor
return files;
}
+
+ uint32_t extractor::decrypt(uint32_t magic, std::vector &data) const
+ {
+ return magic;
+ }
}
diff --git a/src/tests/framework/observer.cpp b/src/tests/framework/observer.cpp
index 5d80282..127930a 100644
--- a/src/tests/framework/observer.cpp
+++ b/src/tests/framework/observer.cpp
@@ -2,7 +2,6 @@
#include "../../api.h"
#include
-#include
#include
#include
@@ -143,6 +142,7 @@ namespace test
observer::observer()
{
modules_.push_back(std::make_unique("renpy.so"));
+ modules_.push_back(std::make_unique("rpgmaker.so"));
modules_.push_back(std::make_unique("zanzarah.so"));
}
diff --git a/src/tests/rpgmaker.cpp b/src/tests/rpgmaker.cpp
new file mode 100644
index 0000000..3e7840c
--- /dev/null
+++ b/src/tests/rpgmaker.cpp
@@ -0,0 +1,50 @@
+#include
+
+#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");
+}
\ No newline at end of file