Add HashTool

This is probably my last addition to the C++ version of this code,
at least if an Experimental ~~Rodeo~~ Rewrite doesn't end up panning
out.

This is a tool for generating the HashID values that the game uses.

Maybe later on I might try and see if I can write a multi-threaded
hash cracker, or better yet, a GPU powered one (no better way to
try and learn GPGPU then actually doing it!), but for now, this is
all you get.

Feel free to lavish in my argument parsing implementation. I just wanted
something simple and fairly easy to read, and this seems to work ok,
while still being platform-independent (no getopt, and I wouldn't need to
drag in additional code to reimplement getopt or such for Windows/Mac/...)
This commit is contained in:
Lily Tsuru 2023-06-07 23:04:33 -04:00
parent d244d5e3c2
commit 1398b8f816
2 changed files with 134 additions and 1 deletions

View File

@ -14,4 +14,12 @@ target_link_libraries(jmmt_pack_extractor PUBLIC jmmt)
set_target_properties(jmmt_pack_extractor PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
)
)
add_executable(jmmt_hashtool jmmt_hashtool.cpp)
target_link_libraries(jmmt_hashtool PUBLIC jmmt)
set_target_properties(jmmt_hashtool PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
)

125
src/tools/jmmt_hashtool.cpp Normal file
View File

@ -0,0 +1,125 @@
// JMMT HashTool
#include <jmmt/crc.h>
#include <array>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <string_view>
struct Arguments {
enum class OutputMode {
Hex, ///< Hexadecimal output.
Decimal ///< Decimal output.
};
char* hashName {};
bool useCase { false };
OutputMode outMode { OutputMode::Hex };
/** Parse arguments from the main() argv. **/
static Arguments FromArgv(int argc, char** argv) {
Arguments args;
args.progname = argv[0];
// no options provided
if(argc == 1) {
args.DispHelp();
std::exit(1);
}
// non-pepper getopt(). I'm too lazy to make it any better though
for(int i = 1; i < argc; ++i) {
if(argv[i][0] == '-' && argv[i][1] != '\0') {
char sw = argv[i][1];
switch(sw) {
// flag options
case 'c':
args.useCase = true;
break;
case 'd':
if(args.outMode == OutputMode::Hex)
args.outMode = OutputMode::Decimal;
break;
case 'h':
if(args.outMode == OutputMode::Decimal)
args.outMode = OutputMode::Hex;
break;
// terminals
case '?':
args.DispHelp();
std::exit(0);
default:
std::printf("Unknown command-line switch '-%c'\n", sw);
args.DispHelp();
std::exit(1);
}
} else {
// Assume any non-positional argument is what we're supposed to hash
args.hashName = argv[i];
}
}
return args;
}
bool Validate() const {
if(!hashName) {
std::printf("No hash name provided\n");
DispHelp();
return false;
}
return true;
}
private:
char* progname {};
void DispHelp() const {
// no I'm not sorry
std::printf(
// clang-format off
"JMMT HashTool - a thing for generating TECH HashID's\n"
"Usage: %s [-c] [-?] <hash object>\n"
" -c Use case-senstive HashID variant (default is case-insensitive)\n"
" -d Output in decimal (default hex)\n"
" -h Output as hexadecimal (if previously overridden; kinda pointless)\n"
" -? Show this help message (and exit)\n",
progname
// clang-format on
);
}
};
int main(int argc, char** argv) {
auto args = Arguments::FromArgv(argc, argv);
if(!args.Validate()) {
return 1;
}
jmmt::crc32_t result {};
if(args.useCase)
result = jmmt::HashStringCase(args.hashName);
else
result = jmmt::HashString(args.hashName);
// clang-format off
using enum Arguments::OutputMode;
switch(args.outMode) {
case Decimal: std::printf("%d\n", result); break;
case Hex: std::printf("0x%08x\n", result); break;
#ifdef __GNUC__
// Mark this path explicitly as UB
default: __builtin_unreachable();
#endif
}
// clang-format on
return 0;
}