-
Notifications
You must be signed in to change notification settings - Fork 1
DLL Plugins
A common issue among many parser tools is that their file support is limited to what the tool is compiled to have - i.e. adding support for other files requires an update to the entire tool, which usually depends on the person who originally created it.
Therefore, the only parsing this tool is hard-coded to actually do is for the common structure of all CyberConnect2 XFBIN files. For unpacking and repacking XFBIN binary chunk data (known internally as nuccChunkBinary
), external plugins are used instead to be downloaded separately. This means that:
- File support can be added to the tool by anyone at any time without re-releasing the entire tool, meaning it's much easier for anyone to contribute indirectly.
- Users only need to download what they actually need, and the main tool itself is fairly lightweight as a result. This will only become more apparent as more and more file support is added - particularly for a variety of CyberConnect2 games, as one user is unlikely to need support for them all.
For the DLL (Dynamic-Link Library) plugins, the tool has a C interface, meaning it supports DLLs compiled from any compiler and even any language as long as the data types they return work with the C programming language (which most languages should support because C is everywhere, obvious or not).
The other option this tool will support (eventually) is Lua scripting. Although it'll be a lot more limited than using DLLs, it will do just fine for basic data structures and will also be both a lot lighter and easier to edit!
As time goes on, maybe people will release guides on creating DLLs for this tool with languages like Rust, Python, and even C itself; but for now, I can only really speak for C++.
The following are all the functions you can use from dll.h
to ease the DLL-creation process.
These functions are entry points for the tool, called by the tool. This is where the plugin authors will write most of their code.
Get_Plugin_Metadata()
-
What? → Returns a
plugin_metadata
struct object with information about the DLL plugin itself. - Why? → So the tool knows whether the plugin matches the input file or not.
plugin_metadata Get_Plugin_Metadata();
Unpack()
-
What? → Instructions for how to unpack/deserialise/decode the plugin's corresponding
nuccChunkBinary
data to JSON.
const char* Unpack(const char* binary_data);
Repack()
-
What? → Instructions for how to repack/serialise/encode the plugin's corresponding JSON data to
nuccChunkBinary
.
const char* Repack(const char* json_data);
These simply handle the transferral of your C++ data types to C-compatible data types.
struct plugin_metadata
- What? → A struct containing array data for which games and paths the plugin is for, and the sizes for those arrays.
- Why? → The tool will use this data to determine which plugins are for which games and binary paths.
struct plugin_metadata {
const char** games;
size_t games_count;
const char** paths;
size_t paths_count;
};
Create_Metadata()
-
What? → Creates and returns a
plugin_metadata
struct object, transferringstd::vector<std::string
supported game and path data toconst char**
andsize_t
on the heap (dynamically-allocated). - Why? → So the tool knows whether the plugin matches the input file or not.
plugin_metadata Create_Metadata(std::vector<std::string>& v_games, std::vector<std::string>& v_paths) {
plugin_metadata metadata;
metadata.games_count = v_games.size(); // Store number of items in `v_games`.
metadata.games = new const char*[metadata.games_count]; // Create an array to store the list of supported games within.
for (auto i = 0; i < metadata.games_count; i++) {
std::string& item = v_games[i];
char* c_str = new char[item.length() + 1];
std::strcpy(c_str, item.c_str());
metadata.games[i] = c_str;
}
metadata.paths_count = v_paths.size(); // Store number of items in `v_paths`.
metadata.paths = new const char*[metadata.paths_count]; // Create an array to store the list of supported paths within.
for (auto i = 0; i < metadata.paths_count; i++) {
std::string& item = v_paths[i];
char* c_str = new char[item.length() + 1];
std::strcpy(c_str, item.c_str());
metadata.paths[i] = c_str;
}
return metadata;
}
JSON_to_C_Str()
- What? → Dumps JSON data to a dynamically-allocated C string and returns it.
- Why? → The tool will convert this back to JSON on the other end.
char* JSON_to_C_Str(json& json_obj) {
char* json_str = (char*)malloc(json_obj.dump().length() + 1); // Allocate the JSON string to the heap.
strcpy(json_str, json_obj.dump().c_str());
return json_str;
}
Clean_Up()
- What? → Called by the tool, frees memory on the heap from all the pointers returned by the main plugin functions.
- Why? → Prevents memory leaks. This function is only to be called by the tool itself.
void Clean_Up(const char** a_ptrs, int a_size, const char** b_ptrs, int b_size, const char* json_ptr) {
for (int i = 0; i < a_size; i++) {
delete(a_ptrs[i]);
}
delete(a_ptrs);
for (int i = 0; i < b_size; i++) {
delete(b_ptrs[i]);
}
delete(b_ptrs);
delete(json_ptr);
}
These are involved in the parsing of data.
Custom Types
- Why? → Mostly just short-handing existing C/C++ types for ease and clarity.
void Clean_Up(const char** a_ptrs, int a_size, const char** b_ptrs, int b_size, const char* json_ptr) {
for (int i = 0; i < a_size; i++) {
delete(a_ptrs[i]);
}
delete(a_ptrs);
for (int i = 0; i < b_size; i++) {
delete(b_ptrs[i]);
}
delete(b_ptrs);
delete(json_ptr);
}
Firstly, I should mention that although C++ appears daunting for a lot of non-programmers, the small library (dll.h
) I've created around the creation of DLLs for this tool specifically makes it a lot easier in my opinion; so in other words, give it a try!
Here is a working example for unpacking the All-Star Battle R sndcmnparam
data, which I believe isn't too difficult to follow:
const char* Unpack(const char* data) {
/* GLOBALS */
chunk_data = data;
chunk_pos = 0;
/* DEFINE VARIABLES */
using std::endian::big;
using std::endian::little;
auto size = Parse<uint32>(big); // big → Total file size.
auto count = Parse<uint16>(little); // little → Entry count.
std::vector<string> entries; // List of audio name IDs.
for (auto i = 0; i < count; i++) {
entries.push_back(Parse<string>(32));
}
/* DEFINE JSON */
json output_json = {
{"Entry Count", count}
};
for (auto i = 0; i < count; i++) {
output_json["Entry " + str(i + 1)] = entries[i];
}
/* Allocate memory for string and return. */
return JSON_to_C_Str(output_json);
}
Currently, I can still see ways it can be made more intuative, which I'll aim to resolve using C++'s amazing class
features soon enough.
The following documentation is how to use my dll.h
header to easily and effectively create DLL plugins for the tool. If you have suggestions for how the header or this system can be improved, feel free to shoot me them! I want to get this as perfect and consistent as possible.
First of all, you'll need to download dll.h
. For now at least, this is a completely-standalone file that doesn't require the rest of the tool's source code to compile. You will, however, need the C++ standard library of course.
I use the GCC compiler with the following console command to compile:
g++ src/files/plugins/[file].cpp src/logger.cpp -o sndcmnparam.dll -shared -std=c++23
Notice that I use C++23, which is primarily for the use of std::byteswap
(converting to different endians). If for whatever reason you don't have access to C++23, you may want to consider downloading GCC. Otherwise, I may release a version of the header that only depends on nothing later than C++17 at some point.
I also include src/logger.cpp
from the tool's source code for the sake of debugging, although you don't need to if you don't want to.
Next, to set up your CPP file, you can copy the following template from [TBA]. It contains the recommended structure of your CPP file, with the functions and whatnot ready for you.
First and most crucial is the Get_Plugin_Metadata()
function. Instead of searching for plugins by name, which would be incredibly unreliable, the tool instead checks each DLL individually until it finds (or doesn't find) one that matches the intended game and type of nuccChunkBinary
data.
The basic structure of this function is as follows:
plugin_metadata Get_Plugin_Metadata() {
std::vector<std::string> games = { /* games */ };
std::vector<std::string> paths = { /* paths */ };
return Create_Metadata(games, paths);
}
The return value, plugin_metadata
is a struct which the Create_Metadata()
function automatically reads to, copying the std::vector<std::string>
values to const char**
and size_t
for C instead.
// From dll.h
struct plugin_metadata {
const char** games;
size_t games_count;
const char** paths;
size_t paths_count;
};
Because of this, defining basic metadata is very simple. For example:
plugin_metadata Get_Plugin_Metadata() {
std::vector<std::string> games = { "ASB", "EOH", "ASBR" };
std::vector<std::string> paths = { "PlayerColorParam.bin", "playerColorParam.bin" };
return Create_Metadata(games, paths);
}
Here is the official list of game IDs and their corresponding games. They may differ a bit from what communities actually use, but the goal here is consistency:
-
ASB
→ JoJo's Bizarre Adventure: All-Star Battle -
EOH
→ JoJo's Bizarre Adventure: Eyes of Heaven -
ASBR
→ JoJo's Bizarre Adventure: All-Star Battle R -
NSUNS3
→ Naruto Shippuden: Ultimate Ninja Storm 3 -
NSUNSR
→ Naruto Shippuden: Ultimate Ninja Storm Revolution -
NSUNS4
→ Naruto Shippuden: Ultimate Ninja Storm 4 -
NXBUNSC
→ NARUTO X BORUTO Ultimate Ninja STORM CONNECTIONS
Feel free to request more game support. Obviously, only games that use XFBINs are applicable.
In the case of some games, particularly the more recent ones, there are versions of some files that are updated in each game version. Because of this, you can use the Regex_Add_Paths()
to avoid having to type out each path individually. For example:
plugin_metadata Get_Plugin_Metadata() {
std::vector<std::string> games = { "ASBR" };
std::vector<std::string> paths;
std::vector<std::string> regex_paths = {
"cmnparam/bin/XXX/battle.bin",
"cmnparam/bin/XXX/commse0.bin",
"cmnparam/bin/XXX/gimmick.bin",
"cmnparam/bin/XXX/player.bin",
"cmnparam/bin/XXX/sd01a_mob.bin",
"cmnparam/bin/XXX/sd02a_mob.bin",
"cmnparam/bin/XXX/sd03b_mob.bin",
"cmnparam/bin/XXX/sd04a_mob.bin",
"cmnparam/bin/XXX/sd04b_mob.bin",
"cmnparam/bin/XXX/sd05a_mob.bin",
"cmnparam/bin/XXX/sd06a_mob.bin",
"cmnparam/bin/XXX/sd06b_mob.bin",
"cmnparam/bin/XXX/sd07a_mob.bin",
"cmnparam/bin/XXX/sd08a_mob.bin"
};
std::vector<std::string> supported_versions = {"100", "110", "130", "140", "150", "160", "170", "200", "210", "220", "230"};
Regex_Add_Paths(paths, "XXX", regex_paths, supported_versions);
return Create_Metadata(games, paths);
}
This will replace all instances of "XXX"
with each supported version and add those to the paths
vector (e.g. one value might be "cmnparam/bin/170/player.bin"
).
The paths in question are the file paths found inside XFBIN files.
More specific information on all these variables and functions can be found in [TBA]. Also, as always, you are free to do whatever custom code you want to get to your wanted result. In that case though, if there's something useful that could be added to dll.h
, please let me know.