Skip to content

Commit

Permalink
RAHasher: hash multiple files via wildcards (#397)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jamiras authored Jan 12, 2024
1 parent a39e131 commit cc39985
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 57 deletions.
2 changes: 1 addition & 1 deletion src/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1354,7 +1354,7 @@ bool Application::unloadGame()

if (_gamePathIsTemporary)
{
_logger.debug(TAG "Deleting temporary content %s", _gamePath);
_logger.debug(TAG "Deleting temporary content %s", _gamePath.c_str());
util::deleteFile(_gamePath);
_gamePathIsTemporary = false;
}
Expand Down
273 changes: 217 additions & 56 deletions src/RAHasher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,21 @@
#include <rcheevos/include/rc_hash.h>

#include <memory>
#include <stdio.h>
#include <string.h>

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#elif defined(__unix__)
#include <glob.h>
#include <sys/stat.h>
#else
#include <dirent.h>
#include <fnmatch.h>
#include <sys/stat.h>
#endif

#ifdef HAVE_CHD
void rc_hash_init_chd_cdreader(); /* in HashCHD.cpp */
#endif
Expand All @@ -22,7 +35,7 @@ static void usage(const char* appname)
printf("\n");
printf(" -v (optional) enables verbose messages for debugging\n");
printf(" systemid specifies the system id associated to the game (which hash algorithm to use)\n");
printf(" filepath specifies the path to the game file\n");
printf(" filepath specifies the path to the game file (file may include wildcards, path may not)\n");
}

class StdErrLogger : public Logger
Expand Down Expand Up @@ -70,89 +83,237 @@ static void* rhash_file_open(const char* path)

#define RC_CONSOLE_MAX 90

int main(int argc, char* argv[])
static int process_file(int consoleId, const std::string& file)
{
int consoleId = 0;
std::string file;
char hash[33];
int result = 1;

if (argc == 3)
std::string filePath = util::fullPath(file);
std::string ext = util::extension(file);

if (consoleId != RC_CONSOLE_ARCADE && consoleId <= RC_CONSOLE_MAX && ext.length() == 4 &&
tolower(ext[1]) == 'z' && tolower(ext[2]) == 'i' && tolower(ext[3]) == 'p')
{
consoleId = atoi(argv[1]);
file = argv[2];
std::string unzippedFilename;
size_t size;
void* data = util::loadZippedFile(logger.get(), filePath, &size, unzippedFilename);
if (data)
{
if (rc_hash_generate_from_buffer(hash, consoleId, (uint8_t*)data, size))
printf("%s", hash);

free(data);
}
}
else if (argc == 4 && strcmp(argv[1], "-v") == 0)
else
{
rc_hash_init_verbose_message_callback(rhash_log);
/* register a custom file_open handler for unicode support. use the default implementation for the other methods */
struct rc_hash_filereader filereader;
memset(&filereader, 0, sizeof(filereader));
filereader.open = rhash_file_open;
rc_hash_init_custom_filereader(&filereader);

if (ext.length() == 4 && tolower(ext[1]) == 'c' && tolower(ext[2]) == 'h' && tolower(ext[3]) == 'd')
{
#ifdef HAVE_CHD
rc_hash_init_chd_cdreader();
#else
printf("CHD not supported without HAVE_CHD compile flag");
return 0;
#endif
}
else
{
rc_hash_init_default_cdreader();
}

consoleId = atoi(argv[2]);
file = argv[3];
if (consoleId > RC_CONSOLE_MAX)
{
rc_hash_iterator iterator;
rc_hash_initialize_iterator(&iterator, filePath.c_str(), NULL, 0);
while (rc_hash_iterate(hash, &iterator))
printf("%s", hash);
rc_hash_destroy_iterator(&iterator);
}
else
{
if (rc_hash_generate_from_file(hash, consoleId, filePath.c_str()))
printf("%s", hash);
}
}

if (consoleId != 0 && !file.empty())
{
file = util::fullPath(file);
return result;
}

logger.reset(new StdErrLogger);
static int process_iterated_file(int console_id, const std::string& file)
{
int result = process_file(console_id, file);
if (!result)
printf("????????????????????????????????");

rc_hash_init_error_message_callback(rhash_log_error);
printf(" %s\n", util::fileNameWithExtension(file).c_str());
return result;
}

std::string ext = util::extension(file);
if (consoleId != RC_CONSOLE_ARCADE && consoleId <= RC_CONSOLE_MAX && ext.length() == 4 &&
tolower(ext[1]) == 'z' && tolower(ext[2]) == 'i' && tolower(ext[3]) == 'p')
static int process_files(int consoleId, const std::string& pattern)
{
int count = 0;

#ifdef _WIN32
const std::string path = util::directory(pattern);
WIN32_FIND_DATAA fileData;
HANDLE hFind;

hFind = FindFirstFileA(pattern.c_str(), &fileData);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
std::string unzippedFilename;
size_t size;
void* data = util::loadZippedFile(logger.get(), file, &size, unzippedFilename);
if (data)
if (!(fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
if (rc_hash_generate_from_buffer(hash, consoleId, (uint8_t*)data, size))
printf("%s\n", hash);

free(data);
const std::string filePath = path + "\\" + fileData.cFileName;
count += process_iterated_file(consoleId, filePath);
}
}
else
} while (FindNextFileA(hFind, &fileData));

FindClose(hFind);
}
#elif defined(__unix__)
glob_t globResult;
memset(&globResult, 0, sizeof(globResult));

if (glob(pattern.c_str(), GLOB_TILDE, NULL, &globResult) == 0)
{
struct stat filebuf;
size_t i;
for (i = 0; i < globResult.gl_pathc; ++i)
{
/* register a custom file_open handler for unicode support. use the default implementation for the other methods */
struct rc_hash_filereader filereader;
memset(&filereader, 0, sizeof(filereader));
filereader.open = rhash_file_open;
rc_hash_init_custom_filereader(&filereader);
if (stat(globResult.gl_pathv[i], &filebuf) == 0 && !S_ISDIR(filebuf.st_mode))
count += process_iterated_file(consoleId, globResult.gl_pathv[i]);
}
}

if (ext.length() == 4 && tolower(ext[1]) == 'c' && tolower(ext[2]) == 'h' && tolower(ext[3]) == 'd')
{
#ifdef HAVE_CHD
rc_hash_init_chd_cdreader();
globfree(&globResult);
#else
printf("CHD not supported without HAVE_CHD compile flag\n");
return 0;
#endif
}
else
const std::string filePattern = util::fileNameWithExtension(pattern);
char resolved_path[PATH_MAX];
std::string path = util::directory(pattern);
DIR* dirp;

realpath(path.c_str(), resolved_path);
path = resolved_path;

dirp = opendir(path.c_str());
if (dirp)
{
struct stat filebuf;
struct dirent *dp;

while ((dp = readdir(dirp)))
{
if (fnmatch(filePattern.c_str(), dp->d_name, 0) == 0)
{
rc_hash_init_default_cdreader();
if (stat(dp->d_name, &filebuf) == 0 && !S_ISDIR(filebuf.st_mode))
{
const std::string filePath = path + "/" + dp->d_name;
count += process_iterated_file(consoleId, filePath);
}
}
}
}
#endif

if (count == 0)
printf("No matches found\n");

return count;
}

int main(int argc, char* argv[])
{
int consoleId = 0;
int singleFile = 1;

int argi = 1;

while (argv[argi][0] == '-')
{
if (strcmp(argv[argi], "-v") == 0)
{
rc_hash_init_verbose_message_callback(rhash_log);
++argi;
}
}

if (argi + 2 > argc)
{
usage(argv[0]);
return 1;
}

consoleId = atoi(argv[argi++]);
if (consoleId == 0)
{
usage(argv[0]);
return 1;
}

logger.reset(new StdErrLogger);
rc_hash_init_error_message_callback(rhash_log_error);

if (argi + 1 < argc)
{
if (consoleId > RC_CONSOLE_MAX)
{
printf("Specific console must be specified when processing multiple files\n");
return 0;
}

singleFile = 0;
}
else
{
std::string file = argv[argi];
if (file.find('*') != std::string::npos || file.find('?') != std::string::npos)
{
if (consoleId > RC_CONSOLE_MAX)
{
rc_hash_iterator iterator;
rc_hash_initialize_iterator(&iterator, file.c_str(), NULL, 0);
while (rc_hash_iterate(hash, &iterator))
printf("%s\n", hash);
rc_hash_destroy_iterator(&iterator);
}
else
{
if (rc_hash_generate_from_file(hash, consoleId, file.c_str()))
printf("%s\n", hash);
printf("Specific console must be specified when using wildcards\n");
return 0;
}

singleFile = 0;
}
}

return result;
if (!singleFile)
{
/* verbose logging not allowed when processing multiple files */
rc_hash_init_verbose_message_callback(NULL);
}

while (argi < argc)
{
std::string file = argv[argi++];

if (file.find('*') != std::string::npos || file.find('?') != std::string::npos)
{
if (!process_files(consoleId, file))
return 0;
}
else
{
int result = process_file(consoleId, file);

if (singleFile)
printf("\n");
else
printf(" %s\n", util::fileNameWithExtension(file).c_str());

if (!result)
return result;
}
}

usage(argv[0]);
return 1;
}

0 comments on commit cc39985

Please sign in to comment.