Skip to content

Commit

Permalink
Rework VFS to use a consistent order when handling multiple files
Browse files Browse the repository at this point in the history
  • Loading branch information
zero318 committed Jul 30, 2024
1 parent eb52120 commit 69edd97
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 68 deletions.
2 changes: 1 addition & 1 deletion Base.props
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
<WarningLevel>Level3</WarningLevel>
<TreatSpecificWarningsAsErrors>4013;4028;4133</TreatSpecificWarningsAsErrors>
<!-- This warning is inside shlobj.h -->
<DisableSpecificWarnings>4091;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<DisableSpecificWarnings>4091;4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<PreprocessorDefinitions>BUILDER_NAME_W=L"$(USERNAME)";UNICODE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/utf-8 /Zc:__cplusplus /Zc:externConstexpr /Zc:threadSafeInit- %(AdditionalOptions)</AdditionalOptions>
Expand Down
78 changes: 78 additions & 0 deletions thcrap/src/strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,81 @@ THCRAP_API const char* strings_strcat(const size_t slot, const char *src);
// don't yet have any specific use cases.
THCRAP_API const char* strings_replace(const size_t slot, const char *src, const char *dst);
/// ------------------

#if __cplusplus

extern "C++" {

template<typename T = void>
struct fixed_string_list {

size_t count;
char** strs;

static inline constexpr size_t NOT_FOUND = size_t_neg_one;

fixed_string_list() = default;

void update_max_count(size_t count) {}

void initialize(const std::vector<std::string_view>& strs_in) {
size_t count = this->count = strs_in.size();
if constexpr (!std::is_same_v<T, void>) {
((T*)this)->update_max_count(count);
}
size_t buffer = 0;
for (const auto& str : strs_in) {
buffer += str.length() + 1;
}
char** strs = this->strs = (char**)malloc(count * sizeof(char*) + buffer);
char* str_raw = (char*)&strs[count];
for (const auto& str : strs_in) {
*strs++ = str_raw;
size_t length = str.length() + 1;
memcpy(str_raw, str.data(), length);
str_raw += length;
}
}

void initialize(char** strs_in, size_t count) {
this->count = count;
if constexpr (!std::is_same_v<T, void>) {
((T*)this)->update_max_count(count);
}
size_t buffer = 0;
for (size_t i = 0; i < count; ++i) {
buffer += strlen(strs_in[i]) + 1;
}
char** strs = this->strs = (char**)malloc(count * sizeof(char*) + buffer);
char* str_raw = (char*)&strs[count];
for (size_t i = 0; i < count; ++i) {
strs[i] = str_raw;
size_t length = strlen(strs_in[i]) + 1;
memcpy(str_raw, strs_in[i], length);
str_raw += length;
}
}

fixed_string_list(const std::vector<std::string_view>& strs) {
this->initialize(strs);
}

fixed_string_list(char** strs, size_t count) {
this->initialize(strs, count);
}

size_t find(std::string_view str) const {
size_t count = this->count;
char** strs = this->strs;
for (size_t i = 0; i < count; ++i) {
if (str == strs[i]) {
return i;
}
}
return NOT_FOUND;
}
};

}

#endif
2 changes: 2 additions & 0 deletions thcrap/src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ static size_t TH_FORCEINLINE strtouz(const char* str, char** str_end, int base)
#endif
}

#define size_t_neg_one (~(size_t)0)

int _vasprintf(char** buffer_ret, const char* format, va_list va);
int _asprintf(char** buffer_ret, const char* format, ...);

Expand Down
64 changes: 39 additions & 25 deletions thcrap/src/vfs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,53 @@

struct jsonvfs_handler_t {
const wchar_t* out_pattern;
std::vector<std::string> in_fns;
jsonvfs_files in_fns;
jsonvfs_generator_t *gen;

jsonvfs_handler_t(const wchar_t* out_pattern, const std::vector<std::string_view>& in_fns, jsonvfs_generator_t* gen)
: out_pattern(out_pattern), in_fns(in_fns), gen(gen)
{}

jsonvfs_handler_t(const wchar_t* out_pattern, char** strs, size_t count, jsonvfs_generator_t* gen)
: out_pattern(out_pattern), in_fns(strs, count), gen(gen)
{}
};

static std::vector<jsonvfs_handler_t> vfs_handlers;

void jsonvfs_add(const char* out_pattern, const std::vector<std::string>& in_fns, jsonvfs_generator_t *gen)
void jsonvfs_add(const char* out_pattern, const std::vector<std::string_view>& in_fns, jsonvfs_generator_t *gen)
{
for (auto& s : in_fns) {
jsondata_add(s.c_str());
jsondata_add(s.data());
}
wchar_t* str = (wchar_t*)utf8_to_utf16(out_pattern);
wstr_slash_normalize(str);
vfs_handlers.push_back({ str, in_fns, gen });
wchar_t* wstr = (wchar_t*)utf8_to_utf16(out_pattern);
wstr_slash_normalize(wstr);
vfs_handlers.emplace_back( wstr, in_fns, gen );
}

void jsonvfs_game_add(const char* out_pattern, const std::vector<std::string>& in_fns, jsonvfs_generator_t *gen)
void jsonvfs_game_add(const char* out_pattern, const std::vector<std::string_view>& in_fns, jsonvfs_generator_t *gen)
{
jsonvfs_handler_t& handler = vfs_handlers.emplace_back();

char* str = fn_for_game(out_pattern);
str_slash_normalize(str);
handler.out_pattern = (wchar_t*)utf8_to_utf16(str);
wchar_t* wstr = (wchar_t*)utf8_to_utf16(str);
free(str);

handler.gen = gen;
size_t count = in_fns.size();
VLA(char*, fns_for_game, count);

for (auto& s : in_fns) {
char* fn = fn_for_game(s.c_str());
handler.in_fns.emplace_back(fn);
for (size_t i = 0; i < count; ++i) {
char* fn = fn_for_game(in_fns[i].data());
fns_for_game[i] = fn;
jsondata_add(fn);
free(fn);
}

vfs_handlers.emplace_back( wstr, fns_for_game, count, gen );

for (size_t i = 0; i < count; ++i) {
free(fns_for_game[i]);
}

VLA_FREE(fns_for_game);
}

json_t *jsonvfs_get(const char* fn, size_t* size)
Expand All @@ -65,15 +79,15 @@ json_t *jsonvfs_get(const char* fn, size_t* size)
VLA(wchar_t, fn_normalized_w, fn_len + 1);
StringToUTF16(fn_normalized_w, fn_normalized, fn_len + 1);

jsonvfs_map* vfs_map = (jsonvfs_map*)_alloca(sizeof(jsonvfs_map) + sizeof(json_t*) * jsonvfs_files::max_count);

for (auto& handler : vfs_handlers) {
if (PathMatchSpecExW(fn_normalized_w, handler.out_pattern, PMSF_NORMAL) == S_OK) {
std::unordered_map<std::string_view, json_t *> in_data;
for (auto& in_fn : handler.in_fns) {
in_data[in_fn] = jsondata_get(in_fn.c_str());
}

new (vfs_map) jsonvfs_map(handler.in_fns);

size_t cur_size = 0;
json_t *new_obj = handler.gen(in_data, fn_view, cur_size);
json_t *new_obj = handler.gen(*vfs_map, fn_view, cur_size);
total_size += cur_size;
obj = json_object_merge(obj, new_obj);
}
Expand Down Expand Up @@ -144,14 +158,14 @@ static json_t *json_map_patch(json_t *map, json_t *in)
return ret;
}

static json_t *map_generator(std::unordered_map<std::string_view, json_t*>& in_data, std::string_view out_fn, size_t& out_size)
static json_t *map_generator(const jsonvfs_map& in_data, std::string_view out_fn, size_t& out_size)
{
const std::string map_fn = std::string(out_fn.substr(0, out_fn.size() - 5)) + "map";
json_t *map = stack_json_resolve_vfs(map_fn.c_str(), &out_size);
if (map) {
json_t *patch_full = nullptr;
for (auto& it : in_data) {
json_t *patch = json_map_patch(map, it.second);
for (auto i : in_data) {
json_t *patch = json_map_patch(map, in_data[i]);
patch_full = json_object_merge(patch_full, patch);
}
return patch_full;
Expand All @@ -160,12 +174,12 @@ static json_t *map_generator(std::unordered_map<std::string_view, json_t*>& in_d

}

void jsonvfs_add_map(const char* out_pattern, const std::vector<std::string>& in_fns)
void jsonvfs_add_map(const char* out_pattern, const std::vector<std::string_view>& in_fns)
{
jsonvfs_add(out_pattern, in_fns, map_generator);
}

void jsonvfs_game_add_map(const char* out_pattern, const std::vector<std::string>& in_fns)
void jsonvfs_game_add_map(const char* out_pattern, const std::vector<std::string_view>& in_fns)
{
jsonvfs_game_add(out_pattern, in_fns, map_generator);
}
83 changes: 78 additions & 5 deletions thcrap/src/vfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,80 @@
#include <unordered_map>
#include <unordered_set>

typedef json_t* jsonvfs_generator_t(std::unordered_map<std::string_view, json_t*>& in_data, std::string_view out_fn, size_t& out_size);
struct jsonvfs_files : public fixed_string_list<jsonvfs_files> {

using fixed_string_list<jsonvfs_files>::fixed_string_list;

static inline size_t max_count = 0;

inline void update_max_count(size_t count) {
if (count > max_count) {
max_count = count;
}
}
};

struct jsonvfs_map_iterator {
size_t index;

jsonvfs_map_iterator(size_t index) : index(index) {}

bool operator!=(jsonvfs_map_iterator rhs) const {
return this->index != rhs.index;
}

jsonvfs_map_iterator& operator++() {
++this->index;
return *this;
}

size_t operator*() const {
return this->index;
}
};

struct jsonvfs_map {
jsonvfs_files files;
json_t* json_data[];

TH_FORCEINLINE jsonvfs_map(const jsonvfs_files& files) {
size_t count = this->files.count = files.count;
char** strs = this->files.strs = files.strs;
for (size_t i = 0; i < count; ++i) {
this->json_data[i] = jsondata_get(strs[i]);
}
}

json_t* find(std::string_view str) const {
size_t index = this->files.find(str);
if (index != fixed_string_list<>::NOT_FOUND) {
return this->json_data[index];
}
return NULL;
}

json_t* operator[](std::string_view str) const {
return this->find(str);
}

json_t* operator[](size_t index) const {
return this->json_data[index];
}

bool empty() const {
return !this->files.count;
}

jsonvfs_map_iterator begin() const {
return 0;
}

jsonvfs_map_iterator end() const {
return this->files.count;
}
};

typedef json_t* jsonvfs_generator_t(const jsonvfs_map& in_data, std::string_view out_fn, size_t& out_size);

extern "C" {
/**
Expand All @@ -26,10 +99,10 @@ extern "C" {
* If it returns a non-NULL value, the return value will be used as if a file
* with this content exists at the top of the patch stack.
*/
THCRAP_API void jsonvfs_add(const char* out_pattern, const std::vector<std::string>& in_fns, jsonvfs_generator_t *gen);
THCRAP_API void jsonvfs_add(const char* out_pattern, const std::vector<std::string_view>& in_fns, jsonvfs_generator_t *gen);

// Same as jsonvfs_add, but all file names are game-relative.
THCRAP_API void jsonvfs_game_add(const char* out_pattern, const std::vector<std::string>& in_fns, jsonvfs_generator_t *gen);
THCRAP_API void jsonvfs_game_add(const char* out_pattern, const std::vector<std::string_view>& in_fns, jsonvfs_generator_t *gen);

/**
* Generate a VFS file from a JSON map file.
Expand All @@ -42,10 +115,10 @@ extern "C" {
* the generated jdiff file will be like this:
* { "key": 5 }
*/
THCRAP_API void jsonvfs_add_map(const char* out_pattern, const std::vector<std::string>& in_fns);
THCRAP_API void jsonvfs_add_map(const char* out_pattern, const std::vector<std::string_view>& in_fns);

// Same as jsonvfs_add_map, but all file names are game-relative.
THCRAP_API void jsonvfs_game_add_map(const char* out_pattern, const std::vector<std::string>& in_fns);
THCRAP_API void jsonvfs_game_add_map(const char* out_pattern, const std::vector<std::string_view>& in_fns);


// Return a file from the vfs if it exists.
Expand Down
15 changes: 5 additions & 10 deletions thcrap_tasofro/src/bgm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include "bgm.h"
#include <algorithm>

json_t* bgm_generator(std::unordered_map<std::string_view, json_t*>& in_data, std::string_view out_fn, size_t& out_size)
json_t* bgm_generator(const jsonvfs_map& in_data, std::string_view out_fn, size_t& out_size)
{
const char *spell_col = nullptr;
const char *comment_col = nullptr;
Expand All @@ -26,18 +26,13 @@ json_t* bgm_generator(std::unordered_map<std::string_view, json_t*>& in_data, st
}

// Find themes.js and musiccmt.js in the input files list
json_t* themes = in_data.find("themes.js");
char* musiccmt_fn = fn_for_game("musiccmt.js");
auto themes_it = in_data.find("themes.js");
auto musiccmt_it = in_data.find(musiccmt_fn);
SAFE_FREE(musiccmt_fn);
if (themes_it == in_data.end() && musiccmt_it == in_data.end()) {
json_t* musiccmt = in_data.find(musiccmt_fn);
free(musiccmt_fn);
if (!themes && !musiccmt) {
return NULL;
}
json_t *themes = themes_it->second;
json_t *musiccmt = nullptr;
if (musiccmt_it != in_data.end()) {
musiccmt = musiccmt_it->second;
}

json_t *ret = json_object();
const char *key;
Expand Down
2 changes: 1 addition & 1 deletion thcrap_tasofro/src/bgm.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
#include <jansson.h>
#include <vfs.h>

json_t* bgm_generator(std::unordered_map<std::string_view, json_t*>& in_data, std::string_view out_fn, size_t& out_size);
json_t* bgm_generator(const jsonvfs_map& in_data, std::string_view out_fn, size_t& out_size);
Loading

0 comments on commit 69edd97

Please sign in to comment.