Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RAHasher: hash multiple files via wildcards #397

Merged
merged 3 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
Loading