Skip to content

Commit

Permalink
The Sacred Tears: TRUE uses a modified map tree
Browse files Browse the repository at this point in the history
Detect this and apply a shift of 1 to all bytes in the map tree to make it work.
  • Loading branch information
Ghabry committed Sep 3, 2024
1 parent 8c269b9 commit 8e45fa7
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ add_library(${PROJECT_NAME} OBJECT
src/fileext_guesser.h
src/filesystem.cpp
src/filesystem.h
src/filesystem_hook.cpp
src/filesystem_hook.h
src/filesystem_lzh.cpp
src/filesystem_lzh.h
src/filesystem_native.cpp
Expand Down
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ libeasyrpg_player_a_SOURCES = \
src/fileext_guesser.h \
src/filesystem.cpp \
src/filesystem.h \
src/filesystem_hook.cpp \
src/filesystem_hook.h \
src/filesystem_lzh.cpp \
src/filesystem_lzh.h \
src/filesystem_native.cpp \
Expand Down
1 change: 1 addition & 0 deletions lib/liblcf-version
Submodule liblcf-version added at 032ccd
145 changes: 145 additions & 0 deletions src/filesystem_hook.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* This file is part of EasyRPG Player.
*
* EasyRPG Player 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.
*
* EasyRPG Player 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 EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
*/

#include "filesystem_hook.h"
#include "filesystem_stream.h"
#include "options.h"

#include <cassert>
#include <cstdio>
#include <streambuf>

class CaesarStreamBuf : public std::streambuf {
public:
CaesarStreamBuf(std::streambuf* parent_sb, int n) : parent_sb(parent_sb), n(n) {
setg(&buf, &buf + 1, &buf + 1);
}
CaesarStreamBuf(CaesarStreamBuf const& other) = delete;
CaesarStreamBuf const& operator=(CaesarStreamBuf const& other) = delete;
~CaesarStreamBuf() {
delete parent_sb;
}

protected:
int_type underflow() override {
auto byte = parent_sb->sbumpc();

if (byte == traits_type::eof()) {
return byte;
}

buf = traits_type::to_char_type(byte);
buf -= n;

setg(&buf, &buf, &buf + 1);

return byte;
}

std::streambuf::pos_type seekoff(std::streambuf::off_type offset, std::ios_base::seekdir dir, std::ios_base::openmode) override {
return parent_sb->pubseekoff(offset, dir);
}

std::streambuf::pos_type seekpos(std::streambuf::pos_type pos, std::ios_base::openmode) override {
return parent_sb->pubseekpos(pos);
}

private:
std::streambuf* parent_sb;

char buf;
int n;
};


HookFilesystem::HookFilesystem(FilesystemView parent_fs, Hook hook) : Filesystem("", parent_fs), active_hook(hook) {
// no-op
}

FilesystemView HookFilesystem::Detect(FilesystemView fs) {
auto lmt = fs.OpenInputStream(TREEMAP_NAME);
std::array<char, 11> buf;

lmt.ReadIntoObj(buf);

if (!memcmp(buf.data(), "\xbMdgNbqUsff", 11)) {
auto hook_fs = std::make_shared<HookFilesystem>(fs, Hook::SacredTears);
return hook_fs->Subtree("");
}

return fs;
}

bool HookFilesystem::IsFile(StringView path) const {
return GetParent().IsFile(path);
}

bool HookFilesystem::IsDirectory(StringView path, bool follow_symlinks) const {
return GetParent().IsDirectory(path, follow_symlinks);
}

bool HookFilesystem::Exists(StringView path) const {
return GetParent().Exists(path);
}

int64_t HookFilesystem::GetFilesize(StringView path) const {
return GetParent().GetFilesize(path);
}

bool HookFilesystem::MakeDirectory(StringView dir, bool follow_symlinks) const {
return GetParent().MakeDirectory(dir, follow_symlinks);
}

bool HookFilesystem::IsFeatureSupported(Feature f) const {
return GetParent().IsFeatureSupported(f);
}

std::streambuf* HookFilesystem::CreateInputStreambuffer(StringView path, std::ios_base::openmode mode) const {
auto parent_sb = GetParent().CreateInputStreambuffer(path, mode);

if (active_hook == Hook::SacredTears) {
if (path == TREEMAP_NAME) {
return new CaesarStreamBuf(parent_sb, 1);
}
}

return parent_sb;
}

std::streambuf* HookFilesystem::CreateOutputStreambuffer(StringView path, std::ios_base::openmode mode) const {
return GetParent().CreateOutputStreambuffer(path, mode);
}

bool HookFilesystem::GetDirectoryContent(StringView path, std::vector<DirectoryTree::Entry>& tree) const {
auto dir_tree = GetParent().ListDirectory(path);

if (!dir_tree) {
return false;
}

for (auto& item: *dir_tree) {
tree.push_back(item.second);
}

return true;
}

std::string HookFilesystem::Describe() const {
assert(active_hook == Hook::SacredTears);

return fmt::format("[Hook] ({})", "Sacred Tears");
}
60 changes: 60 additions & 0 deletions src/filesystem_hook.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* This file is part of EasyRPG Player.
*
* EasyRPG Player 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.
*
* EasyRPG Player 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 EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef EP_FILESYSTEM_HOOK_H
#define EP_FILESYSTEM_HOOK_H

#include "filesystem.h"

/**
* A virtual filesystem that applies modifications to game files
*/
class HookFilesystem : public Filesystem {
public:
enum class Hook {
// The Sacred Tears: TRUE
// Shifts the offset of all bytes in the LMT back by one
SacredTears
};

explicit HookFilesystem(FilesystemView parent_fs, Hook hook);

static FilesystemView Detect(FilesystemView fs);

/**
* Implementation of abstract methods
*/
/** @{ */
bool IsFile(StringView path) const override;
bool IsDirectory(StringView path, bool follow_symlinks) const override;
bool Exists(StringView path) const override;
int64_t GetFilesize(StringView path) const override;
bool MakeDirectory(StringView dir, bool follow_symlinks) const override;
bool IsFeatureSupported(Feature f) const override;
std::string Describe() const override;
/** @} */
protected:
/** @{ */
bool GetDirectoryContent(StringView path, std::vector<DirectoryTree::Entry>& entries) const override;
std::streambuf* CreateInputStreambuffer(StringView path, std::ios_base::openmode mode) const override;
std::streambuf* CreateOutputStreambuffer(StringView path, std::ios_base::openmode mode) const override;
/** @} */

Hook active_hook;
};

#endif
5 changes: 4 additions & 1 deletion src/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#include <iomanip>
#include <fstream>
#include <memory>
#include <thread>

#ifdef _WIN32
# include "platform/windows/utils.h"
Expand All @@ -42,6 +41,7 @@
#include "filefinder.h"
#include "filefinder_rtp.h"
#include "fileext_guesser.h"
#include "filesystem_hook.h"
#include "game_actors.h"
#include "game_battle.h"
#include "game_map.h"
Expand Down Expand Up @@ -692,6 +692,9 @@ void Player::CreateGameObjects() {
}
escape_char = Utils::DecodeUTF32(Player::escape_symbol).front();

// Special handling for games with altered files
FileFinder::SetGameFilesystem(HookFilesystem::Detect(FileFinder::Game()));

// Check for translation-related directories and load language names.
translation.InitTranslations();

Expand Down

0 comments on commit 8e45fa7

Please sign in to comment.