gemba 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/THIRD_PARTY_NOTICES +37 -2
- data/assets/placeholder_boxart.png +0 -0
- data/bin/gemba +2 -2
- data/ext/gemba/extconf.rb +23 -1
- data/ext/gemba/gemba_ext.c +436 -2
- data/ext/gemba/gemba_ext.h +2 -0
- data/gemba.gemspec +5 -3
- data/lib/gemba/achievements/achievement.rb +23 -0
- data/lib/gemba/achievements/backend.rb +186 -0
- data/lib/gemba/achievements/cache.rb +70 -0
- data/lib/gemba/achievements/credentials_presenter.rb +142 -0
- data/lib/gemba/achievements/fake_backend.rb +205 -0
- data/lib/gemba/achievements/null_backend.rb +11 -0
- data/lib/gemba/achievements/offline_backend.rb +168 -0
- data/lib/gemba/achievements/retro_achievements/backend.rb +453 -0
- data/lib/gemba/achievements/retro_achievements/cli_sync_requester.rb +64 -0
- data/lib/gemba/achievements/retro_achievements/ping_worker.rb +27 -0
- data/lib/gemba/achievements.rb +19 -0
- data/lib/gemba/achievements_window.rb +556 -0
- data/lib/gemba/app_controller.rb +1015 -0
- data/lib/gemba/bios.rb +54 -0
- data/lib/gemba/boxart_fetcher/libretro_backend.rb +39 -0
- data/lib/gemba/boxart_fetcher/null_backend.rb +12 -0
- data/lib/gemba/boxart_fetcher.rb +79 -0
- data/lib/gemba/bus_emitter.rb +13 -0
- data/lib/gemba/child_window.rb +24 -1
- data/lib/gemba/cli/commands/config_cmd.rb +83 -0
- data/lib/gemba/cli/commands/decode.rb +154 -0
- data/lib/gemba/cli/commands/patch.rb +78 -0
- data/lib/gemba/cli/commands/play.rb +78 -0
- data/lib/gemba/cli/commands/record.rb +114 -0
- data/lib/gemba/cli/commands/replay.rb +161 -0
- data/lib/gemba/cli/commands/retro_achievements.rb +213 -0
- data/lib/gemba/cli/commands/version.rb +22 -0
- data/lib/gemba/cli.rb +52 -364
- data/lib/gemba/config.rb +134 -1
- data/lib/gemba/data/gb_games.json +1 -0
- data/lib/gemba/data/gb_md5.json +1 -0
- data/lib/gemba/data/gba_games.json +1 -0
- data/lib/gemba/data/gba_md5.json +1 -0
- data/lib/gemba/data/gbc_games.json +1 -0
- data/lib/gemba/data/gbc_md5.json +1 -0
- data/lib/gemba/emulator_frame.rb +1060 -0
- data/lib/gemba/event_bus.rb +48 -0
- data/lib/gemba/frame_stack.rb +60 -0
- data/lib/gemba/game_index.rb +84 -0
- data/lib/gemba/game_picker_frame.rb +268 -0
- data/lib/gemba/gamepad_map.rb +103 -0
- data/lib/gemba/headless.rb +6 -5
- data/lib/gemba/headless_player.rb +33 -3
- data/lib/gemba/help_window.rb +61 -0
- data/lib/gemba/hotkey_map.rb +3 -1
- data/lib/gemba/input_recorder.rb +107 -0
- data/lib/gemba/input_replayer.rb +119 -0
- data/lib/gemba/keyboard_map.rb +90 -0
- data/lib/gemba/locales/en.yml +97 -5
- data/lib/gemba/locales/ja.yml +97 -5
- data/lib/gemba/main_window.rb +56 -0
- data/lib/gemba/modal_stack.rb +81 -0
- data/lib/gemba/patcher_window.rb +223 -0
- data/lib/gemba/platform/gb.rb +21 -0
- data/lib/gemba/platform/gba.rb +21 -0
- data/lib/gemba/platform/gbc.rb +23 -0
- data/lib/gemba/platform.rb +20 -0
- data/lib/gemba/platform_open.rb +19 -0
- data/lib/gemba/recorder.rb +4 -3
- data/lib/gemba/replay_player.rb +691 -0
- data/lib/gemba/rom_info.rb +57 -0
- data/lib/gemba/rom_info_window.rb +16 -3
- data/lib/gemba/rom_library.rb +106 -0
- data/lib/gemba/rom_overrides.rb +47 -0
- data/lib/gemba/rom_patcher/bps.rb +161 -0
- data/lib/gemba/rom_patcher/ips.rb +101 -0
- data/lib/gemba/rom_patcher/ups.rb +118 -0
- data/lib/gemba/rom_patcher.rb +109 -0
- data/lib/gemba/{rom_loader.rb → rom_resolver.rb} +7 -6
- data/lib/gemba/runtime.rb +59 -26
- data/lib/gemba/save_state_manager.rb +4 -7
- data/lib/gemba/save_state_picker.rb +17 -4
- data/lib/gemba/session_logger.rb +64 -0
- data/lib/gemba/settings/audio_tab.rb +77 -0
- data/lib/gemba/settings/gamepad_tab.rb +351 -0
- data/lib/gemba/settings/hotkeys_tab.rb +259 -0
- data/lib/gemba/settings/paths.rb +11 -0
- data/lib/gemba/settings/recording_tab.rb +83 -0
- data/lib/gemba/settings/save_states_tab.rb +91 -0
- data/lib/gemba/settings/system_tab.rb +362 -0
- data/lib/gemba/settings/video_tab.rb +318 -0
- data/lib/gemba/settings_window.rb +162 -1036
- data/lib/gemba/version.rb +1 -1
- data/lib/gemba/virtual_keyboard.rb +19 -0
- data/lib/gemba.rb +2 -12
- data/test/achievements_window/test_bulk_sync.rb +218 -0
- data/test/achievements_window/test_bus_events.rb +125 -0
- data/test/achievements_window/test_close_confirmation.rb +201 -0
- data/test/achievements_window/test_initial_state.rb +164 -0
- data/test/achievements_window/test_sorting.rb +227 -0
- data/test/achievements_window/test_tree_rendering.rb +133 -0
- data/test/fixtures/fake_bios.bin +0 -0
- data/test/fixtures/pong.gba +0 -0
- data/test/fixtures/test.gb +0 -0
- data/test/fixtures/test.gbc +0 -0
- data/test/fixtures/test_quicksave.ss +0 -0
- data/test/screenshots/no_focus.png +0 -0
- data/test/shared/teek_test_worker.rb +17 -1
- data/test/shared/tk_test_helper.rb +91 -4
- data/test/support/achievements_window_helpers.rb +18 -0
- data/test/support/fake_core.rb +25 -0
- data/test/support/fake_ra_runtime.rb +74 -0
- data/test/support/fake_requester.rb +68 -0
- data/test/support/player_helpers.rb +20 -5
- data/test/test_achievement.rb +32 -0
- data/test/{test_player.rb → test_app_controller.rb} +353 -85
- data/test/test_bios.rb +123 -0
- data/test/test_boxart_fetcher.rb +150 -0
- data/test/test_cli.rb +17 -265
- data/test/test_cli_config.rb +64 -0
- data/test/test_cli_decode.rb +97 -0
- data/test/test_cli_patch.rb +58 -0
- data/test/test_cli_play.rb +213 -0
- data/test/test_cli_ra.rb +175 -0
- data/test/test_cli_record.rb +69 -0
- data/test/test_cli_replay.rb +72 -0
- data/test/test_cli_sync_requester.rb +152 -0
- data/test/test_cli_version.rb +27 -0
- data/test/test_config.rb +2 -3
- data/test/test_config_ra.rb +69 -0
- data/test/test_core.rb +62 -1
- data/test/test_credentials_presenter.rb +192 -0
- data/test/test_event_bus.rb +100 -0
- data/test/test_fake_backend_achievements.rb +130 -0
- data/test/test_fake_backend_auth.rb +68 -0
- data/test/test_game_index.rb +77 -0
- data/test/test_game_picker_frame.rb +310 -0
- data/test/test_gamepad_map.rb +1 -3
- data/test/test_headless_player.rb +17 -3
- data/test/test_help_window.rb +82 -0
- data/test/test_hotkey_map.rb +22 -1
- data/test/test_input_recorder.rb +179 -0
- data/test/test_input_replay_determinism.rb +113 -0
- data/test/test_input_replayer.rb +162 -0
- data/test/test_keyboard_map.rb +1 -3
- data/test/test_libretro_backend.rb +41 -0
- data/test/test_locale.rb +1 -1
- data/test/test_logging.rb +123 -0
- data/test/test_null_backend.rb +42 -0
- data/test/test_offline_backend.rb +116 -0
- data/test/test_overlay_renderer.rb +1 -1
- data/test/test_platform.rb +149 -0
- data/test/test_ra_backend.rb +313 -0
- data/test/test_ra_backend_unlock_gate.rb +56 -0
- data/test/test_recorder.rb +0 -3
- data/test/test_replay_player.rb +316 -0
- data/test/test_rom_info.rb +149 -0
- data/test/test_rom_overrides.rb +86 -0
- data/test/test_rom_patcher.rb +382 -0
- data/test/{test_rom_loader.rb → test_rom_resolver.rb} +25 -26
- data/test/test_save_state_manager.rb +2 -4
- data/test/test_settings_audio.rb +107 -0
- data/test/test_settings_hotkeys.rb +83 -66
- data/test/test_settings_recording.rb +49 -0
- data/test/test_settings_save_states.rb +97 -0
- data/test/test_settings_system.rb +133 -0
- data/test/test_settings_video.rb +450 -0
- data/test/test_settings_window.rb +76 -507
- data/test/test_tip_service.rb +6 -6
- data/test/test_toast_overlay.rb +1 -1
- data/test/test_virtual_events.rb +156 -0
- data/test/test_virtual_keyboard.rb +1 -1
- data/vendor/rcheevos/CHANGELOG.md +495 -0
- data/vendor/rcheevos/LICENSE +21 -0
- data/vendor/rcheevos/Package.swift +33 -0
- data/vendor/rcheevos/README.md +67 -0
- data/vendor/rcheevos/include/module.modulemap +70 -0
- data/vendor/rcheevos/include/rc_api_editor.h +296 -0
- data/vendor/rcheevos/include/rc_api_info.h +280 -0
- data/vendor/rcheevos/include/rc_api_request.h +77 -0
- data/vendor/rcheevos/include/rc_api_runtime.h +417 -0
- data/vendor/rcheevos/include/rc_api_user.h +262 -0
- data/vendor/rcheevos/include/rc_client.h +877 -0
- data/vendor/rcheevos/include/rc_client_raintegration.h +101 -0
- data/vendor/rcheevos/include/rc_consoles.h +138 -0
- data/vendor/rcheevos/include/rc_error.h +59 -0
- data/vendor/rcheevos/include/rc_export.h +100 -0
- data/vendor/rcheevos/include/rc_hash.h +200 -0
- data/vendor/rcheevos/include/rc_runtime.h +148 -0
- data/vendor/rcheevos/include/rc_runtime_types.h +452 -0
- data/vendor/rcheevos/include/rc_util.h +51 -0
- data/vendor/rcheevos/include/rcheevos.h +8 -0
- data/vendor/rcheevos/src/rapi/rc_api_common.c +1379 -0
- data/vendor/rcheevos/src/rapi/rc_api_common.h +88 -0
- data/vendor/rcheevos/src/rapi/rc_api_editor.c +625 -0
- data/vendor/rcheevos/src/rapi/rc_api_info.c +587 -0
- data/vendor/rcheevos/src/rapi/rc_api_runtime.c +901 -0
- data/vendor/rcheevos/src/rapi/rc_api_user.c +483 -0
- data/vendor/rcheevos/src/rc_client.c +6941 -0
- data/vendor/rcheevos/src/rc_client_external.c +281 -0
- data/vendor/rcheevos/src/rc_client_external.h +177 -0
- data/vendor/rcheevos/src/rc_client_external_versions.h +171 -0
- data/vendor/rcheevos/src/rc_client_internal.h +409 -0
- data/vendor/rcheevos/src/rc_client_raintegration.c +566 -0
- data/vendor/rcheevos/src/rc_client_raintegration_internal.h +61 -0
- data/vendor/rcheevos/src/rc_client_types.natvis +396 -0
- data/vendor/rcheevos/src/rc_compat.c +251 -0
- data/vendor/rcheevos/src/rc_compat.h +121 -0
- data/vendor/rcheevos/src/rc_libretro.c +915 -0
- data/vendor/rcheevos/src/rc_libretro.h +98 -0
- data/vendor/rcheevos/src/rc_util.c +199 -0
- data/vendor/rcheevos/src/rc_version.c +11 -0
- data/vendor/rcheevos/src/rc_version.h +32 -0
- data/vendor/rcheevos/src/rcheevos/alloc.c +312 -0
- data/vendor/rcheevos/src/rcheevos/condition.c +754 -0
- data/vendor/rcheevos/src/rcheevos/condset.c +777 -0
- data/vendor/rcheevos/src/rcheevos/consoleinfo.c +1215 -0
- data/vendor/rcheevos/src/rcheevos/format.c +330 -0
- data/vendor/rcheevos/src/rcheevos/lboard.c +287 -0
- data/vendor/rcheevos/src/rcheevos/memref.c +805 -0
- data/vendor/rcheevos/src/rcheevos/operand.c +607 -0
- data/vendor/rcheevos/src/rcheevos/rc_internal.h +390 -0
- data/vendor/rcheevos/src/rcheevos/rc_runtime_types.natvis +541 -0
- data/vendor/rcheevos/src/rcheevos/rc_validate.c +1406 -0
- data/vendor/rcheevos/src/rcheevos/rc_validate.h +18 -0
- data/vendor/rcheevos/src/rcheevos/richpresence.c +922 -0
- data/vendor/rcheevos/src/rcheevos/runtime.c +852 -0
- data/vendor/rcheevos/src/rcheevos/runtime_progress.c +1073 -0
- data/vendor/rcheevos/src/rcheevos/trigger.c +344 -0
- data/vendor/rcheevos/src/rcheevos/value.c +935 -0
- data/vendor/rcheevos/src/rhash/aes.c +480 -0
- data/vendor/rcheevos/src/rhash/aes.h +49 -0
- data/vendor/rcheevos/src/rhash/cdreader.c +838 -0
- data/vendor/rcheevos/src/rhash/hash.c +1402 -0
- data/vendor/rcheevos/src/rhash/hash_disc.c +1340 -0
- data/vendor/rcheevos/src/rhash/hash_encrypted.c +566 -0
- data/vendor/rcheevos/src/rhash/hash_rom.c +426 -0
- data/vendor/rcheevos/src/rhash/hash_zip.c +460 -0
- data/vendor/rcheevos/src/rhash/md5.c +382 -0
- data/vendor/rcheevos/src/rhash/md5.h +91 -0
- data/vendor/rcheevos/src/rhash/rc_hash_internal.h +116 -0
- data/vendor/rcheevos/test/libretro.h +205 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_common.c +941 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_editor.c +931 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_info.c +545 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_runtime.c +2213 -0
- data/vendor/rcheevos/test/rapi/test_rc_api_user.c +998 -0
- data/vendor/rcheevos/test/rcheevos/mock_memory.h +32 -0
- data/vendor/rcheevos/test/rcheevos/test_condition.c +570 -0
- data/vendor/rcheevos/test/rcheevos/test_condset.c +5170 -0
- data/vendor/rcheevos/test/rcheevos/test_consoleinfo.c +203 -0
- data/vendor/rcheevos/test/rcheevos/test_format.c +112 -0
- data/vendor/rcheevos/test/rcheevos/test_lboard.c +746 -0
- data/vendor/rcheevos/test/rcheevos/test_memref.c +520 -0
- data/vendor/rcheevos/test/rcheevos/test_operand.c +692 -0
- data/vendor/rcheevos/test/rcheevos/test_rc_validate.c +502 -0
- data/vendor/rcheevos/test/rcheevos/test_richpresence.c +1564 -0
- data/vendor/rcheevos/test/rcheevos/test_runtime.c +1667 -0
- data/vendor/rcheevos/test/rcheevos/test_runtime_progress.c +1821 -0
- data/vendor/rcheevos/test/rcheevos/test_timing.c +166 -0
- data/vendor/rcheevos/test/rcheevos/test_trigger.c +2521 -0
- data/vendor/rcheevos/test/rcheevos/test_value.c +870 -0
- data/vendor/rcheevos/test/rcheevos-test.sln +46 -0
- data/vendor/rcheevos/test/rcheevos-test.vcxproj +239 -0
- data/vendor/rcheevos/test/rcheevos-test.vcxproj.filters +335 -0
- data/vendor/rcheevos/test/rhash/data.c +657 -0
- data/vendor/rcheevos/test/rhash/data.h +32 -0
- data/vendor/rcheevos/test/rhash/mock_filereader.c +236 -0
- data/vendor/rcheevos/test/rhash/mock_filereader.h +31 -0
- data/vendor/rcheevos/test/rhash/test_cdreader.c +920 -0
- data/vendor/rcheevos/test/rhash/test_hash.c +310 -0
- data/vendor/rcheevos/test/rhash/test_hash_disc.c +1450 -0
- data/vendor/rcheevos/test/rhash/test_hash_rom.c +899 -0
- data/vendor/rcheevos/test/rhash/test_hash_zip.c +551 -0
- data/vendor/rcheevos/test/test.c +113 -0
- data/vendor/rcheevos/test/test_framework.h +205 -0
- data/vendor/rcheevos/test/test_rc_client.c +10509 -0
- data/vendor/rcheevos/test/test_rc_client_external.c +2197 -0
- data/vendor/rcheevos/test/test_rc_client_raintegration.c +441 -0
- data/vendor/rcheevos/test/test_rc_libretro.c +952 -0
- data/vendor/rcheevos/test/test_types.natvis +9 -0
- data/vendor/rcheevos/validator/validator.c +658 -0
- data/vendor/rcheevos/validator/validator.vcxproj +152 -0
- data/vendor/rcheevos/validator/validator.vcxproj.filters +82 -0
- metadata +274 -11
- data/lib/gemba/input_mappings.rb +0 -214
- data/lib/gemba/player.rb +0 -1525
|
@@ -0,0 +1,1402 @@
|
|
|
1
|
+
#include "rc_hash.h"
|
|
2
|
+
|
|
3
|
+
#include "rc_hash_internal.h"
|
|
4
|
+
|
|
5
|
+
#include "../rc_compat.h"
|
|
6
|
+
|
|
7
|
+
#if defined(_WIN32)
|
|
8
|
+
#define WIN32_LEAN_AND_MEAN
|
|
9
|
+
#include <windows.h>
|
|
10
|
+
#include <share.h>
|
|
11
|
+
#endif
|
|
12
|
+
|
|
13
|
+
#include <ctype.h>
|
|
14
|
+
#include <stdarg.h>
|
|
15
|
+
|
|
16
|
+
const char* rc_path_get_filename(const char* path);
|
|
17
|
+
static int rc_hash_from_file(char hash[33], uint32_t console_id, const rc_hash_iterator_t* iterator);
|
|
18
|
+
|
|
19
|
+
/* ===================================================== */
|
|
20
|
+
|
|
21
|
+
static rc_hash_message_callback_deprecated g_error_message_callback = NULL;
|
|
22
|
+
static rc_hash_message_callback_deprecated g_verbose_message_callback = NULL;
|
|
23
|
+
|
|
24
|
+
static void rc_hash_call_g_error_message_callback(const char* message, const rc_hash_iterator_t* iterator)
|
|
25
|
+
{
|
|
26
|
+
(void)iterator;
|
|
27
|
+
g_error_message_callback(message);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static void rc_hash_call_g_verbose_message_callback(const char* message, const rc_hash_iterator_t* iterator)
|
|
31
|
+
{
|
|
32
|
+
(void)iterator;
|
|
33
|
+
g_verbose_message_callback(message);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static void rc_hash_dispatch_message_va(const rc_hash_message_callback_func callback,
|
|
37
|
+
const rc_hash_iterator_t* iterator, const char* format, va_list args)
|
|
38
|
+
{
|
|
39
|
+
char buffer[1024];
|
|
40
|
+
|
|
41
|
+
#ifdef __STDC_SECURE_LIB__
|
|
42
|
+
vsprintf_s(buffer, sizeof(buffer), format, args);
|
|
43
|
+
#elif __STDC_VERSION__ >= 199901L /* vsnprintf requires c99 */
|
|
44
|
+
vsnprintf(buffer, sizeof(buffer), format, args);
|
|
45
|
+
#else /* c89 doesn't have a size-limited vsprintf function - assume the buffer is large enough */
|
|
46
|
+
vsprintf(buffer, format, args);
|
|
47
|
+
#endif
|
|
48
|
+
|
|
49
|
+
callback(buffer, iterator);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
void rc_hash_init_error_message_callback(rc_hash_message_callback_deprecated callback)
|
|
53
|
+
{
|
|
54
|
+
g_error_message_callback = callback;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static rc_hash_message_callback_func rc_hash_get_error_message_callback(const rc_hash_callbacks_t* callbacks)
|
|
58
|
+
{
|
|
59
|
+
if (callbacks && callbacks->error_message)
|
|
60
|
+
return callbacks->error_message;
|
|
61
|
+
|
|
62
|
+
if (g_error_message_callback)
|
|
63
|
+
return rc_hash_call_g_error_message_callback;
|
|
64
|
+
|
|
65
|
+
if (callbacks && callbacks->verbose_message)
|
|
66
|
+
return callbacks->verbose_message;
|
|
67
|
+
|
|
68
|
+
if (g_verbose_message_callback)
|
|
69
|
+
return rc_hash_call_g_verbose_message_callback;
|
|
70
|
+
|
|
71
|
+
return NULL;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
int rc_hash_iterator_error(const rc_hash_iterator_t* iterator, const char* message)
|
|
75
|
+
{
|
|
76
|
+
rc_hash_message_callback_func message_callback = rc_hash_get_error_message_callback(&iterator->callbacks);
|
|
77
|
+
|
|
78
|
+
if (message_callback)
|
|
79
|
+
message_callback(message, iterator);
|
|
80
|
+
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
int rc_hash_iterator_error_formatted(const rc_hash_iterator_t* iterator, const char* format, ...)
|
|
85
|
+
{
|
|
86
|
+
rc_hash_message_callback_func message_callback = rc_hash_get_error_message_callback(&iterator->callbacks);
|
|
87
|
+
|
|
88
|
+
if (message_callback) {
|
|
89
|
+
va_list args;
|
|
90
|
+
va_start(args, format);
|
|
91
|
+
rc_hash_dispatch_message_va(message_callback, iterator, format, args);
|
|
92
|
+
va_end(args);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
void rc_hash_init_verbose_message_callback(rc_hash_message_callback_deprecated callback)
|
|
99
|
+
{
|
|
100
|
+
g_verbose_message_callback = callback;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
void rc_hash_iterator_verbose(const rc_hash_iterator_t* iterator, const char* message)
|
|
104
|
+
{
|
|
105
|
+
if (iterator->callbacks.verbose_message)
|
|
106
|
+
iterator->callbacks.verbose_message(message, iterator);
|
|
107
|
+
else if (g_verbose_message_callback)
|
|
108
|
+
g_verbose_message_callback(message);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
void rc_hash_iterator_verbose_formatted(const rc_hash_iterator_t* iterator, const char* format, ...)
|
|
112
|
+
{
|
|
113
|
+
if (iterator->callbacks.verbose_message) {
|
|
114
|
+
va_list args;
|
|
115
|
+
va_start(args, format);
|
|
116
|
+
rc_hash_dispatch_message_va(iterator->callbacks.verbose_message, iterator, format, args);
|
|
117
|
+
va_end(args);
|
|
118
|
+
}
|
|
119
|
+
else if (g_verbose_message_callback) {
|
|
120
|
+
va_list args;
|
|
121
|
+
va_start(args, format);
|
|
122
|
+
rc_hash_dispatch_message_va(rc_hash_call_g_verbose_message_callback, iterator, format, args);
|
|
123
|
+
va_end(args);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* ===================================================== */
|
|
128
|
+
|
|
129
|
+
static struct rc_hash_filereader g_filereader_funcs;
|
|
130
|
+
static struct rc_hash_filereader* g_filereader = NULL;
|
|
131
|
+
|
|
132
|
+
#if defined(WINVER) && WINVER >= 0x0500
|
|
133
|
+
static void* filereader_open(const char* path)
|
|
134
|
+
{
|
|
135
|
+
/* Windows requires using wchar APIs for Unicode paths */
|
|
136
|
+
/* Note that MultiByteToWideChar will only be defined for >= Windows 2000 */
|
|
137
|
+
wchar_t* wpath;
|
|
138
|
+
int wpath_length;
|
|
139
|
+
FILE* fp;
|
|
140
|
+
|
|
141
|
+
/* Calculate wpath length from path */
|
|
142
|
+
wpath_length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path, -1, NULL, 0);
|
|
143
|
+
if (wpath_length == 0) /* 0 indicates error (this is likely from invalid UTF-8) */
|
|
144
|
+
return NULL;
|
|
145
|
+
|
|
146
|
+
wpath = (wchar_t*)malloc(wpath_length * sizeof(wchar_t));
|
|
147
|
+
if (!wpath)
|
|
148
|
+
return NULL;
|
|
149
|
+
|
|
150
|
+
if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, wpath_length) == 0) {
|
|
151
|
+
free(wpath);
|
|
152
|
+
return NULL;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
#if defined(__STDC_SECURE_LIB__)
|
|
156
|
+
/* have to use _SH_DENYNO because some cores lock the file while its loaded */
|
|
157
|
+
fp = _wfsopen(wpath, L"rb", _SH_DENYNO);
|
|
158
|
+
#else
|
|
159
|
+
fp = _wfopen(wpath, L"rb");
|
|
160
|
+
#endif
|
|
161
|
+
|
|
162
|
+
free(wpath);
|
|
163
|
+
return fp;
|
|
164
|
+
}
|
|
165
|
+
#else /* !WINVER >= 0x0500 */
|
|
166
|
+
static void* filereader_open(const char* path)
|
|
167
|
+
{
|
|
168
|
+
#if defined(__STDC_SECURE_LIB__)
|
|
169
|
+
#if defined(WINVER)
|
|
170
|
+
/* have to use _SH_DENYNO because some cores lock the file while its loaded */
|
|
171
|
+
return _fsopen(path, "rb", _SH_DENYNO);
|
|
172
|
+
#else /* !WINVER */
|
|
173
|
+
FILE *fp;
|
|
174
|
+
fopen_s(&fp, path, "rb");
|
|
175
|
+
return fp;
|
|
176
|
+
#endif
|
|
177
|
+
#else /* !__STDC_SECURE_LIB__ */
|
|
178
|
+
return fopen(path, "rb");
|
|
179
|
+
#endif
|
|
180
|
+
}
|
|
181
|
+
#endif /* WINVER >= 0x0500 */
|
|
182
|
+
|
|
183
|
+
static void filereader_seek(void* file_handle, int64_t offset, int origin)
|
|
184
|
+
{
|
|
185
|
+
#if defined(_WIN32)
|
|
186
|
+
_fseeki64((FILE*)file_handle, offset, origin);
|
|
187
|
+
#elif defined(_LARGEFILE64_SOURCE)
|
|
188
|
+
fseeko64((FILE*)file_handle, offset, origin);
|
|
189
|
+
#else
|
|
190
|
+
fseek((FILE*)file_handle, offset, origin);
|
|
191
|
+
#endif
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
static int64_t filereader_tell(void* file_handle)
|
|
195
|
+
{
|
|
196
|
+
#if defined(_WIN32)
|
|
197
|
+
return _ftelli64((FILE*)file_handle);
|
|
198
|
+
#elif defined(_LARGEFILE64_SOURCE)
|
|
199
|
+
return ftello64((FILE*)file_handle);
|
|
200
|
+
#else
|
|
201
|
+
return ftell((FILE*)file_handle);
|
|
202
|
+
#endif
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
static size_t filereader_read(void* file_handle, void* buffer, size_t requested_bytes)
|
|
206
|
+
{
|
|
207
|
+
return fread(buffer, 1, requested_bytes, (FILE*)file_handle);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
static void filereader_close(void* file_handle)
|
|
211
|
+
{
|
|
212
|
+
fclose((FILE*)file_handle);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* for unit tests - normally would call rc_hash_init_custom_filereader(NULL) */
|
|
216
|
+
void rc_hash_reset_filereader(void)
|
|
217
|
+
{
|
|
218
|
+
g_filereader = NULL;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
void rc_hash_init_custom_filereader(struct rc_hash_filereader* reader)
|
|
222
|
+
{
|
|
223
|
+
/* initialize with defaults first */
|
|
224
|
+
g_filereader_funcs.open = filereader_open;
|
|
225
|
+
g_filereader_funcs.seek = filereader_seek;
|
|
226
|
+
g_filereader_funcs.tell = filereader_tell;
|
|
227
|
+
g_filereader_funcs.read = filereader_read;
|
|
228
|
+
g_filereader_funcs.close = filereader_close;
|
|
229
|
+
|
|
230
|
+
/* hook up any provided custom handlers */
|
|
231
|
+
if (reader) {
|
|
232
|
+
if (reader->open)
|
|
233
|
+
g_filereader_funcs.open = reader->open;
|
|
234
|
+
|
|
235
|
+
if (reader->seek)
|
|
236
|
+
g_filereader_funcs.seek = reader->seek;
|
|
237
|
+
|
|
238
|
+
if (reader->tell)
|
|
239
|
+
g_filereader_funcs.tell = reader->tell;
|
|
240
|
+
|
|
241
|
+
if (reader->read)
|
|
242
|
+
g_filereader_funcs.read = reader->read;
|
|
243
|
+
|
|
244
|
+
if (reader->close)
|
|
245
|
+
g_filereader_funcs.close = reader->close;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
g_filereader = &g_filereader_funcs;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
void* rc_file_open(const rc_hash_iterator_t* iterator, const char* path)
|
|
252
|
+
{
|
|
253
|
+
void* handle = NULL;
|
|
254
|
+
|
|
255
|
+
if (!iterator->callbacks.filereader.open) {
|
|
256
|
+
rc_hash_iterator_error(iterator, "No callback registered for opening files");
|
|
257
|
+
} else {
|
|
258
|
+
handle = iterator->callbacks.filereader.open(path);
|
|
259
|
+
if (handle)
|
|
260
|
+
rc_hash_iterator_verbose_formatted(iterator, "Opened %s", rc_path_get_filename(path));
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return handle;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
void rc_file_seek(const rc_hash_iterator_t* iterator, void* file_handle, int64_t offset, int origin)
|
|
267
|
+
{
|
|
268
|
+
if (iterator->callbacks.filereader.seek)
|
|
269
|
+
iterator->callbacks.filereader.seek(file_handle, offset, origin);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
int64_t rc_file_tell(const rc_hash_iterator_t* iterator, void* file_handle)
|
|
273
|
+
{
|
|
274
|
+
return iterator->callbacks.filereader.tell ? iterator->callbacks.filereader.tell(file_handle) : 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
size_t rc_file_read(const rc_hash_iterator_t* iterator, void* file_handle, void* buffer, int requested_bytes)
|
|
278
|
+
{
|
|
279
|
+
return iterator->callbacks.filereader.read ? iterator->callbacks.filereader.read(file_handle, buffer, requested_bytes) : 0;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
void rc_file_close(const rc_hash_iterator_t* iterator, void* file_handle)
|
|
283
|
+
{
|
|
284
|
+
if (iterator->callbacks.filereader.close)
|
|
285
|
+
iterator->callbacks.filereader.close(file_handle);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
int64_t rc_file_size(const rc_hash_iterator_t* iterator, const char* path)
|
|
289
|
+
{
|
|
290
|
+
int64_t size = 0;
|
|
291
|
+
|
|
292
|
+
/* don't use rc_file_open to avoid log statements */
|
|
293
|
+
if (!iterator->callbacks.filereader.open) {
|
|
294
|
+
rc_hash_iterator_error(iterator, "No callback registered for opening files");
|
|
295
|
+
} else {
|
|
296
|
+
void* handle = iterator->callbacks.filereader.open(path);
|
|
297
|
+
if (handle) {
|
|
298
|
+
rc_file_seek(iterator, handle, 0, SEEK_END);
|
|
299
|
+
size = rc_file_tell(iterator, handle);
|
|
300
|
+
rc_file_close(iterator, handle);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return size;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* ===================================================== */
|
|
308
|
+
|
|
309
|
+
const char* rc_path_get_filename(const char* path)
|
|
310
|
+
{
|
|
311
|
+
const char* ptr = path + strlen(path);
|
|
312
|
+
do {
|
|
313
|
+
if (ptr[-1] == '/' || ptr[-1] == '\\')
|
|
314
|
+
break;
|
|
315
|
+
|
|
316
|
+
--ptr;
|
|
317
|
+
} while (ptr > path);
|
|
318
|
+
|
|
319
|
+
return ptr;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const char* rc_path_get_extension(const char* path)
|
|
323
|
+
{
|
|
324
|
+
const char* ptr = path + strlen(path);
|
|
325
|
+
do {
|
|
326
|
+
if (ptr[-1] == '.')
|
|
327
|
+
return ptr;
|
|
328
|
+
|
|
329
|
+
--ptr;
|
|
330
|
+
} while (ptr > path);
|
|
331
|
+
|
|
332
|
+
return path + strlen(path);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
int rc_path_compare_extension(const char* path, const char* ext)
|
|
336
|
+
{
|
|
337
|
+
size_t path_len = strlen(path);
|
|
338
|
+
size_t ext_len = strlen(ext);
|
|
339
|
+
const char* ptr = path + path_len - ext_len;
|
|
340
|
+
if (ptr[-1] != '.')
|
|
341
|
+
return 0;
|
|
342
|
+
|
|
343
|
+
if (memcmp(ptr, ext, ext_len) == 0)
|
|
344
|
+
return 1;
|
|
345
|
+
|
|
346
|
+
do {
|
|
347
|
+
if (tolower(*ptr) != *ext)
|
|
348
|
+
return 0;
|
|
349
|
+
|
|
350
|
+
++ext;
|
|
351
|
+
++ptr;
|
|
352
|
+
} while (*ptr);
|
|
353
|
+
|
|
354
|
+
return 1;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/* ===================================================== */
|
|
358
|
+
|
|
359
|
+
void rc_hash_byteswap16(uint8_t* buffer, const uint8_t* stop)
|
|
360
|
+
{
|
|
361
|
+
uint32_t* ptr = (uint32_t*)buffer;
|
|
362
|
+
const uint32_t* stop32 = (const uint32_t*)stop;
|
|
363
|
+
while (ptr < stop32) {
|
|
364
|
+
uint32_t temp = *ptr;
|
|
365
|
+
temp = (temp & 0xFF00FF00) >> 8 |
|
|
366
|
+
(temp & 0x00FF00FF) << 8;
|
|
367
|
+
*ptr++ = temp;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
void rc_hash_byteswap32(uint8_t* buffer, const uint8_t* stop)
|
|
372
|
+
{
|
|
373
|
+
uint32_t* ptr = (uint32_t*)buffer;
|
|
374
|
+
const uint32_t* stop32 = (const uint32_t*)stop;
|
|
375
|
+
while (ptr < stop32) {
|
|
376
|
+
uint32_t temp = *ptr;
|
|
377
|
+
temp = (temp & 0xFF000000) >> 24 |
|
|
378
|
+
(temp & 0x00FF0000) >> 8 |
|
|
379
|
+
(temp & 0x0000FF00) << 8 |
|
|
380
|
+
(temp & 0x000000FF) << 24;
|
|
381
|
+
*ptr++ = temp;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
int rc_hash_finalize(const rc_hash_iterator_t* iterator, md5_state_t* md5, char hash[33])
|
|
386
|
+
{
|
|
387
|
+
md5_byte_t digest[16];
|
|
388
|
+
|
|
389
|
+
md5_finish(md5, digest);
|
|
390
|
+
|
|
391
|
+
/* NOTE: sizeof(hash) is 4 because it's still treated like a pointer, despite specifying a size */
|
|
392
|
+
snprintf(hash, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
|
393
|
+
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
|
|
394
|
+
digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
rc_hash_iterator_verbose_formatted(iterator, "Generated hash %s", hash);
|
|
398
|
+
|
|
399
|
+
return 1;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
int rc_hash_buffer(char hash[33], const uint8_t* buffer, size_t buffer_size, const rc_hash_iterator_t* iterator)
|
|
403
|
+
{
|
|
404
|
+
md5_state_t md5;
|
|
405
|
+
|
|
406
|
+
if (buffer_size > MAX_BUFFER_SIZE)
|
|
407
|
+
buffer_size = MAX_BUFFER_SIZE;
|
|
408
|
+
|
|
409
|
+
md5_init(&md5);
|
|
410
|
+
|
|
411
|
+
md5_append(&md5, buffer, (int)buffer_size);
|
|
412
|
+
|
|
413
|
+
rc_hash_iterator_verbose_formatted(iterator, "Hashing %u byte buffer", (unsigned)buffer_size);
|
|
414
|
+
|
|
415
|
+
return rc_hash_finalize(iterator, &md5, hash);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
struct rc_buffered_file
|
|
419
|
+
{
|
|
420
|
+
const uint8_t* read_ptr;
|
|
421
|
+
const uint8_t* data;
|
|
422
|
+
size_t data_size;
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
static struct rc_buffered_file rc_buffered_file;
|
|
426
|
+
|
|
427
|
+
static void* rc_file_open_buffered_file(const char* path)
|
|
428
|
+
{
|
|
429
|
+
struct rc_buffered_file* handle = (struct rc_buffered_file*)malloc(sizeof(struct rc_buffered_file));
|
|
430
|
+
(void)path;
|
|
431
|
+
|
|
432
|
+
if (handle)
|
|
433
|
+
memcpy(handle, &rc_buffered_file, sizeof(rc_buffered_file));
|
|
434
|
+
|
|
435
|
+
return handle;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
static void rc_file_seek_buffered_file(void* file_handle, int64_t offset, int origin)
|
|
439
|
+
{
|
|
440
|
+
struct rc_buffered_file* buffered_file = (struct rc_buffered_file*)file_handle;
|
|
441
|
+
switch (origin) {
|
|
442
|
+
case SEEK_SET: buffered_file->read_ptr = buffered_file->data + offset; break;
|
|
443
|
+
case SEEK_CUR: buffered_file->read_ptr += offset; break;
|
|
444
|
+
case SEEK_END: buffered_file->read_ptr = buffered_file->data + buffered_file->data_size + offset; break;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (buffered_file->read_ptr < buffered_file->data)
|
|
448
|
+
buffered_file->read_ptr = buffered_file->data;
|
|
449
|
+
else if (buffered_file->read_ptr > buffered_file->data + buffered_file->data_size)
|
|
450
|
+
buffered_file->read_ptr = buffered_file->data + buffered_file->data_size;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
static int64_t rc_file_tell_buffered_file(void* file_handle)
|
|
454
|
+
{
|
|
455
|
+
struct rc_buffered_file* buffered_file = (struct rc_buffered_file*)file_handle;
|
|
456
|
+
return (buffered_file->read_ptr - buffered_file->data);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
static size_t rc_file_read_buffered_file(void* file_handle, void* buffer, size_t requested_bytes)
|
|
460
|
+
{
|
|
461
|
+
struct rc_buffered_file* buffered_file = (struct rc_buffered_file*)file_handle;
|
|
462
|
+
const int64_t remaining = buffered_file->data_size - (buffered_file->read_ptr - buffered_file->data);
|
|
463
|
+
if ((int)requested_bytes > remaining)
|
|
464
|
+
requested_bytes = (int)remaining;
|
|
465
|
+
|
|
466
|
+
memcpy(buffer, buffered_file->read_ptr, requested_bytes);
|
|
467
|
+
buffered_file->read_ptr += requested_bytes;
|
|
468
|
+
return requested_bytes;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
static void rc_file_close_buffered_file(void* file_handle)
|
|
472
|
+
{
|
|
473
|
+
free(file_handle);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
static int rc_hash_file_from_buffer(char hash[33], uint32_t console_id, const rc_hash_iterator_t* iterator)
|
|
477
|
+
{
|
|
478
|
+
int result;
|
|
479
|
+
|
|
480
|
+
rc_hash_iterator_t buffered_file_iterator;
|
|
481
|
+
memset(&buffered_file_iterator, 0, sizeof(buffered_file_iterator));
|
|
482
|
+
memcpy(&buffered_file_iterator.callbacks, &iterator->callbacks, sizeof(iterator->callbacks));
|
|
483
|
+
buffered_file_iterator.userdata = iterator->userdata;
|
|
484
|
+
|
|
485
|
+
buffered_file_iterator.callbacks.filereader.open = rc_file_open_buffered_file;
|
|
486
|
+
buffered_file_iterator.callbacks.filereader.close = rc_file_close_buffered_file;
|
|
487
|
+
buffered_file_iterator.callbacks.filereader.read = rc_file_read_buffered_file;
|
|
488
|
+
buffered_file_iterator.callbacks.filereader.seek = rc_file_seek_buffered_file;
|
|
489
|
+
buffered_file_iterator.callbacks.filereader.tell = rc_file_tell_buffered_file;
|
|
490
|
+
buffered_file_iterator.path = "memory stream";
|
|
491
|
+
|
|
492
|
+
rc_buffered_file.data = rc_buffered_file.read_ptr = iterator->buffer;
|
|
493
|
+
rc_buffered_file.data_size = iterator->buffer_size;
|
|
494
|
+
|
|
495
|
+
result = rc_hash_from_file(hash, console_id, &buffered_file_iterator);
|
|
496
|
+
|
|
497
|
+
buffered_file_iterator.path = NULL;
|
|
498
|
+
rc_hash_destroy_iterator(&buffered_file_iterator);
|
|
499
|
+
return result;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
static int rc_hash_from_buffer(char hash[33], uint32_t console_id, const rc_hash_iterator_t* iterator)
|
|
503
|
+
{
|
|
504
|
+
switch (console_id) {
|
|
505
|
+
default:
|
|
506
|
+
return rc_hash_iterator_error_formatted(iterator, "Unsupported console for buffer hash: %d", console_id);
|
|
507
|
+
|
|
508
|
+
case RC_CONSOLE_AMSTRAD_PC:
|
|
509
|
+
case RC_CONSOLE_APPLE_II:
|
|
510
|
+
case RC_CONSOLE_ARCADIA_2001:
|
|
511
|
+
case RC_CONSOLE_ATARI_2600:
|
|
512
|
+
case RC_CONSOLE_ATARI_JAGUAR:
|
|
513
|
+
case RC_CONSOLE_COLECOVISION:
|
|
514
|
+
case RC_CONSOLE_COMMODORE_64:
|
|
515
|
+
case RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER:
|
|
516
|
+
case RC_CONSOLE_FAIRCHILD_CHANNEL_F:
|
|
517
|
+
case RC_CONSOLE_GAMEBOY:
|
|
518
|
+
case RC_CONSOLE_GAMEBOY_ADVANCE:
|
|
519
|
+
case RC_CONSOLE_GAMEBOY_COLOR:
|
|
520
|
+
case RC_CONSOLE_GAME_GEAR:
|
|
521
|
+
case RC_CONSOLE_INTELLIVISION:
|
|
522
|
+
case RC_CONSOLE_INTERTON_VC_4000:
|
|
523
|
+
case RC_CONSOLE_MAGNAVOX_ODYSSEY2:
|
|
524
|
+
case RC_CONSOLE_MASTER_SYSTEM:
|
|
525
|
+
case RC_CONSOLE_MEGA_DRIVE:
|
|
526
|
+
case RC_CONSOLE_MEGADUCK:
|
|
527
|
+
case RC_CONSOLE_MSX:
|
|
528
|
+
case RC_CONSOLE_NEOGEO_POCKET:
|
|
529
|
+
case RC_CONSOLE_ORIC:
|
|
530
|
+
case RC_CONSOLE_PC8800:
|
|
531
|
+
case RC_CONSOLE_POKEMON_MINI:
|
|
532
|
+
case RC_CONSOLE_SEGA_32X:
|
|
533
|
+
case RC_CONSOLE_SG1000:
|
|
534
|
+
case RC_CONSOLE_SUPERVISION:
|
|
535
|
+
case RC_CONSOLE_TI83:
|
|
536
|
+
case RC_CONSOLE_TIC80:
|
|
537
|
+
case RC_CONSOLE_UZEBOX:
|
|
538
|
+
case RC_CONSOLE_VECTREX:
|
|
539
|
+
case RC_CONSOLE_VIRTUAL_BOY:
|
|
540
|
+
case RC_CONSOLE_WASM4:
|
|
541
|
+
case RC_CONSOLE_WONDERSWAN:
|
|
542
|
+
case RC_CONSOLE_ZX_SPECTRUM:
|
|
543
|
+
return rc_hash_buffer(hash, iterator->buffer, iterator->buffer_size, iterator);
|
|
544
|
+
|
|
545
|
+
#ifndef RC_HASH_NO_ROM
|
|
546
|
+
case RC_CONSOLE_ARDUBOY:
|
|
547
|
+
return rc_hash_arduboy(hash, iterator);
|
|
548
|
+
|
|
549
|
+
case RC_CONSOLE_ATARI_7800:
|
|
550
|
+
return rc_hash_7800(hash, iterator);
|
|
551
|
+
|
|
552
|
+
case RC_CONSOLE_ATARI_LYNX:
|
|
553
|
+
return rc_hash_lynx(hash, iterator);
|
|
554
|
+
|
|
555
|
+
case RC_CONSOLE_FAMICOM_DISK_SYSTEM:
|
|
556
|
+
case RC_CONSOLE_NINTENDO:
|
|
557
|
+
return rc_hash_nes(hash, iterator);
|
|
558
|
+
|
|
559
|
+
case RC_CONSOLE_PC_ENGINE: /* NOTE: does not support PCEngine CD */
|
|
560
|
+
return rc_hash_pce(hash, iterator);
|
|
561
|
+
|
|
562
|
+
case RC_CONSOLE_SUPER_CASSETTEVISION:
|
|
563
|
+
return rc_hash_scv(hash, iterator);
|
|
564
|
+
|
|
565
|
+
case RC_CONSOLE_SUPER_NINTENDO:
|
|
566
|
+
return rc_hash_snes(hash, iterator);
|
|
567
|
+
#endif
|
|
568
|
+
|
|
569
|
+
case RC_CONSOLE_NINTENDO_64:
|
|
570
|
+
case RC_CONSOLE_NINTENDO_3DS:
|
|
571
|
+
case RC_CONSOLE_NINTENDO_DS:
|
|
572
|
+
case RC_CONSOLE_NINTENDO_DSI:
|
|
573
|
+
return rc_hash_file_from_buffer(hash, console_id, iterator);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
int rc_hash_whole_file(char hash[33], const rc_hash_iterator_t* iterator)
|
|
578
|
+
{
|
|
579
|
+
md5_state_t md5;
|
|
580
|
+
uint8_t* buffer;
|
|
581
|
+
int64_t size;
|
|
582
|
+
const size_t buffer_size = 65536;
|
|
583
|
+
void* file_handle;
|
|
584
|
+
size_t remaining;
|
|
585
|
+
int result = 0;
|
|
586
|
+
|
|
587
|
+
file_handle = rc_file_open(iterator, iterator->path);
|
|
588
|
+
if (!file_handle)
|
|
589
|
+
return rc_hash_iterator_error(iterator, "Could not open file");
|
|
590
|
+
|
|
591
|
+
rc_file_seek(iterator, file_handle, 0, SEEK_END);
|
|
592
|
+
size = rc_file_tell(iterator, file_handle);
|
|
593
|
+
|
|
594
|
+
if (size > MAX_BUFFER_SIZE) {
|
|
595
|
+
rc_hash_iterator_verbose_formatted(iterator, "Hashing first %u bytes (of %u bytes) of %s", MAX_BUFFER_SIZE, (unsigned)size, rc_path_get_filename(iterator->path));
|
|
596
|
+
remaining = MAX_BUFFER_SIZE;
|
|
597
|
+
}
|
|
598
|
+
else {
|
|
599
|
+
rc_hash_iterator_verbose_formatted(iterator, "Hashing %s (%u bytes)", rc_path_get_filename(iterator->path), (unsigned)size);
|
|
600
|
+
remaining = (size_t)size;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
md5_init(&md5);
|
|
604
|
+
|
|
605
|
+
buffer = (uint8_t*)malloc(buffer_size);
|
|
606
|
+
if (buffer) {
|
|
607
|
+
rc_file_seek(iterator, file_handle, 0, SEEK_SET);
|
|
608
|
+
while (remaining >= buffer_size) {
|
|
609
|
+
rc_file_read(iterator, file_handle, buffer, (int)buffer_size);
|
|
610
|
+
md5_append(&md5, buffer, (int)buffer_size);
|
|
611
|
+
remaining -= buffer_size;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if (remaining > 0) {
|
|
615
|
+
rc_file_read(iterator, file_handle, buffer, (int)remaining);
|
|
616
|
+
md5_append(&md5, buffer, (int)remaining);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
free(buffer);
|
|
620
|
+
result = rc_hash_finalize(iterator, &md5, hash);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
rc_file_close(iterator, file_handle);
|
|
624
|
+
return result;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
int rc_hash_buffered_file(char hash[33], uint32_t console_id, const rc_hash_iterator_t* iterator)
|
|
628
|
+
{
|
|
629
|
+
uint8_t* buffer;
|
|
630
|
+
int64_t size;
|
|
631
|
+
int result = 0;
|
|
632
|
+
void* file_handle;
|
|
633
|
+
|
|
634
|
+
file_handle = rc_file_open(iterator, iterator->path);
|
|
635
|
+
if (!file_handle)
|
|
636
|
+
return rc_hash_iterator_error(iterator, "Could not open file");
|
|
637
|
+
|
|
638
|
+
rc_file_seek(iterator, file_handle, 0, SEEK_END);
|
|
639
|
+
size = rc_file_tell(iterator, file_handle);
|
|
640
|
+
|
|
641
|
+
if (size > MAX_BUFFER_SIZE) {
|
|
642
|
+
rc_hash_iterator_verbose_formatted(iterator, "Buffering first %u bytes (of %d bytes) of %s", MAX_BUFFER_SIZE, (unsigned)size, rc_path_get_filename(iterator->path));
|
|
643
|
+
size = MAX_BUFFER_SIZE;
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
rc_hash_iterator_verbose_formatted(iterator, "Buffering %s (%d bytes)", rc_path_get_filename(iterator->path), (unsigned)size);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
buffer = (uint8_t*)malloc((size_t)size);
|
|
650
|
+
if (buffer) {
|
|
651
|
+
rc_hash_iterator_t buffer_iterator;
|
|
652
|
+
memset(&buffer_iterator, 0, sizeof(buffer_iterator));
|
|
653
|
+
memcpy(&buffer_iterator.callbacks, &iterator->callbacks, sizeof(iterator->callbacks));
|
|
654
|
+
buffer_iterator.userdata = iterator->userdata;
|
|
655
|
+
buffer_iterator.path = iterator->path;
|
|
656
|
+
buffer_iterator.buffer = buffer;
|
|
657
|
+
buffer_iterator.buffer_size = (size_t)size;
|
|
658
|
+
|
|
659
|
+
rc_file_seek(iterator, file_handle, 0, SEEK_SET);
|
|
660
|
+
rc_file_read(iterator, file_handle, buffer, (int)size);
|
|
661
|
+
|
|
662
|
+
result = rc_hash_from_buffer(hash, console_id, &buffer_iterator);
|
|
663
|
+
|
|
664
|
+
free(buffer);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
rc_file_close(iterator, file_handle);
|
|
668
|
+
return result;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
static int rc_hash_path_is_absolute(const char* path)
|
|
672
|
+
{
|
|
673
|
+
if (!path[0])
|
|
674
|
+
return 0;
|
|
675
|
+
|
|
676
|
+
/* "/path/to/file" or "\path\to\file" */
|
|
677
|
+
if (path[0] == '/' || path[0] == '\\')
|
|
678
|
+
return 1;
|
|
679
|
+
|
|
680
|
+
/* "C:\path\to\file" */
|
|
681
|
+
if (path[1] == ':' && path[2] == '\\')
|
|
682
|
+
return 1;
|
|
683
|
+
|
|
684
|
+
/* "scheme:/path/to/file" */
|
|
685
|
+
while (*path) {
|
|
686
|
+
if (path[0] == ':' && path[1] == '/')
|
|
687
|
+
return 1;
|
|
688
|
+
|
|
689
|
+
++path;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
return 0;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
static const char* rc_hash_get_first_item_from_playlist(const rc_hash_iterator_t* iterator) {
|
|
696
|
+
char buffer[1024];
|
|
697
|
+
char* disc_path;
|
|
698
|
+
char* ptr, *start, *next;
|
|
699
|
+
size_t num_read, path_len, file_len;
|
|
700
|
+
void* file_handle;
|
|
701
|
+
|
|
702
|
+
file_handle = rc_file_open(iterator, iterator->path);
|
|
703
|
+
if (!file_handle) {
|
|
704
|
+
rc_hash_iterator_error(iterator, "Could not open playlist");
|
|
705
|
+
return NULL;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
num_read = rc_file_read(iterator, file_handle, buffer, sizeof(buffer) - 1);
|
|
709
|
+
buffer[num_read] = '\0';
|
|
710
|
+
|
|
711
|
+
rc_file_close(iterator, file_handle);
|
|
712
|
+
|
|
713
|
+
ptr = start = buffer;
|
|
714
|
+
do {
|
|
715
|
+
/* ignore empty and commented lines */
|
|
716
|
+
while (*ptr == '#' || *ptr == '\r' || *ptr == '\n') {
|
|
717
|
+
while (*ptr && *ptr != '\n')
|
|
718
|
+
++ptr;
|
|
719
|
+
if (*ptr)
|
|
720
|
+
++ptr;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/* find and extract the current line */
|
|
724
|
+
start = ptr;
|
|
725
|
+
while (*ptr && *ptr != '\n')
|
|
726
|
+
++ptr;
|
|
727
|
+
next = ptr;
|
|
728
|
+
|
|
729
|
+
/* remove trailing whitespace - especially '\r' */
|
|
730
|
+
while (ptr > start && isspace((unsigned char)ptr[-1]))
|
|
731
|
+
--ptr;
|
|
732
|
+
|
|
733
|
+
/* if we found a non-empty line, break out of the loop to process it */
|
|
734
|
+
file_len = ptr - start;
|
|
735
|
+
if (file_len)
|
|
736
|
+
break;
|
|
737
|
+
|
|
738
|
+
/* did we reach the end of the file? */
|
|
739
|
+
if (!*next)
|
|
740
|
+
return NULL;
|
|
741
|
+
|
|
742
|
+
/* if the line only contained whitespace, keep searching */
|
|
743
|
+
ptr = next + 1;
|
|
744
|
+
} while (1);
|
|
745
|
+
|
|
746
|
+
rc_hash_iterator_verbose_formatted(iterator, "Extracted %.*s from playlist", (int)file_len, start);
|
|
747
|
+
|
|
748
|
+
start[file_len++] = '\0';
|
|
749
|
+
if (rc_hash_path_is_absolute(start))
|
|
750
|
+
path_len = 0;
|
|
751
|
+
else
|
|
752
|
+
path_len = rc_path_get_filename(iterator->path) - iterator->path;
|
|
753
|
+
|
|
754
|
+
disc_path = (char*)malloc(path_len + file_len + 1);
|
|
755
|
+
if (!disc_path)
|
|
756
|
+
return NULL;
|
|
757
|
+
|
|
758
|
+
if (path_len)
|
|
759
|
+
memcpy(disc_path, iterator->path, path_len);
|
|
760
|
+
|
|
761
|
+
memcpy(&disc_path[path_len], start, file_len);
|
|
762
|
+
return disc_path;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
static int rc_hash_generate_from_playlist(char hash[33], uint32_t console_id, const rc_hash_iterator_t* iterator) {
|
|
766
|
+
rc_hash_iterator_t first_file_iterator;
|
|
767
|
+
const char* disc_path;
|
|
768
|
+
int result;
|
|
769
|
+
|
|
770
|
+
rc_hash_iterator_verbose_formatted(iterator, "Processing playlist: %s", rc_path_get_filename(iterator->path));
|
|
771
|
+
|
|
772
|
+
disc_path = rc_hash_get_first_item_from_playlist(iterator);
|
|
773
|
+
if (!disc_path)
|
|
774
|
+
return rc_hash_iterator_error(iterator, "Failed to get first item from playlist");
|
|
775
|
+
|
|
776
|
+
memset(&first_file_iterator, 0, sizeof(first_file_iterator));
|
|
777
|
+
memcpy(&first_file_iterator.callbacks, &iterator->callbacks, sizeof(iterator->callbacks));
|
|
778
|
+
first_file_iterator.userdata = iterator->userdata;
|
|
779
|
+
first_file_iterator.path = disc_path; /* rc_hash_destory_iterator will free */
|
|
780
|
+
|
|
781
|
+
result = rc_hash_from_file(hash, console_id, &first_file_iterator);
|
|
782
|
+
|
|
783
|
+
rc_hash_destroy_iterator(&first_file_iterator);
|
|
784
|
+
return result;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
static int rc_hash_from_file(char hash[33], uint32_t console_id, const rc_hash_iterator_t* iterator)
|
|
788
|
+
{
|
|
789
|
+
const char* path = iterator->path;
|
|
790
|
+
|
|
791
|
+
switch (console_id) {
|
|
792
|
+
default:
|
|
793
|
+
return rc_hash_iterator_error_formatted(iterator, "Unsupported console for file hash: %d", console_id);
|
|
794
|
+
|
|
795
|
+
case RC_CONSOLE_ARCADIA_2001:
|
|
796
|
+
case RC_CONSOLE_ATARI_2600:
|
|
797
|
+
case RC_CONSOLE_ATARI_JAGUAR:
|
|
798
|
+
case RC_CONSOLE_COLECOVISION:
|
|
799
|
+
case RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER:
|
|
800
|
+
case RC_CONSOLE_FAIRCHILD_CHANNEL_F:
|
|
801
|
+
case RC_CONSOLE_GAMEBOY:
|
|
802
|
+
case RC_CONSOLE_GAMEBOY_ADVANCE:
|
|
803
|
+
case RC_CONSOLE_GAMEBOY_COLOR:
|
|
804
|
+
case RC_CONSOLE_GAME_GEAR:
|
|
805
|
+
case RC_CONSOLE_INTELLIVISION:
|
|
806
|
+
case RC_CONSOLE_INTERTON_VC_4000:
|
|
807
|
+
case RC_CONSOLE_MAGNAVOX_ODYSSEY2:
|
|
808
|
+
case RC_CONSOLE_MASTER_SYSTEM:
|
|
809
|
+
case RC_CONSOLE_MEGADUCK:
|
|
810
|
+
case RC_CONSOLE_NEOGEO_POCKET:
|
|
811
|
+
case RC_CONSOLE_ORIC:
|
|
812
|
+
case RC_CONSOLE_POKEMON_MINI:
|
|
813
|
+
case RC_CONSOLE_SEGA_32X:
|
|
814
|
+
case RC_CONSOLE_SG1000:
|
|
815
|
+
case RC_CONSOLE_SUPERVISION:
|
|
816
|
+
case RC_CONSOLE_TI83:
|
|
817
|
+
case RC_CONSOLE_TIC80:
|
|
818
|
+
case RC_CONSOLE_UZEBOX:
|
|
819
|
+
case RC_CONSOLE_VECTREX:
|
|
820
|
+
case RC_CONSOLE_VIRTUAL_BOY:
|
|
821
|
+
case RC_CONSOLE_WASM4:
|
|
822
|
+
case RC_CONSOLE_WONDERSWAN:
|
|
823
|
+
case RC_CONSOLE_ZX_SPECTRUM:
|
|
824
|
+
/* generic whole-file hash - don't buffer */
|
|
825
|
+
return rc_hash_whole_file(hash, iterator);
|
|
826
|
+
|
|
827
|
+
case RC_CONSOLE_MEGA_DRIVE:
|
|
828
|
+
/* generic whole-file hash with m3u support - don't buffer */
|
|
829
|
+
if (rc_path_compare_extension(path, "m3u"))
|
|
830
|
+
return rc_hash_generate_from_playlist(hash, console_id, iterator);
|
|
831
|
+
|
|
832
|
+
return rc_hash_whole_file(hash, iterator);
|
|
833
|
+
|
|
834
|
+
case RC_CONSOLE_ATARI_7800:
|
|
835
|
+
case RC_CONSOLE_ATARI_LYNX:
|
|
836
|
+
case RC_CONSOLE_FAMICOM_DISK_SYSTEM:
|
|
837
|
+
case RC_CONSOLE_NINTENDO:
|
|
838
|
+
case RC_CONSOLE_PC_ENGINE:
|
|
839
|
+
case RC_CONSOLE_SUPER_CASSETTEVISION:
|
|
840
|
+
case RC_CONSOLE_SUPER_NINTENDO:
|
|
841
|
+
/* additional logic whole-file hash - buffer then call rc_hash_generate_from_buffer */
|
|
842
|
+
return rc_hash_buffered_file(hash, console_id, iterator);
|
|
843
|
+
|
|
844
|
+
case RC_CONSOLE_AMSTRAD_PC:
|
|
845
|
+
case RC_CONSOLE_APPLE_II:
|
|
846
|
+
case RC_CONSOLE_COMMODORE_64:
|
|
847
|
+
case RC_CONSOLE_MSX:
|
|
848
|
+
case RC_CONSOLE_PC8800:
|
|
849
|
+
/* generic whole-file hash with m3u support - don't buffer */
|
|
850
|
+
if (rc_path_compare_extension(path, "m3u"))
|
|
851
|
+
return rc_hash_generate_from_playlist(hash, console_id, iterator);
|
|
852
|
+
|
|
853
|
+
return rc_hash_whole_file(hash, iterator);
|
|
854
|
+
|
|
855
|
+
#ifndef RC_HASH_NO_DISC
|
|
856
|
+
case RC_CONSOLE_3DO:
|
|
857
|
+
if (rc_path_compare_extension(path, "m3u"))
|
|
858
|
+
return rc_hash_generate_from_playlist(hash, console_id, iterator);
|
|
859
|
+
|
|
860
|
+
return rc_hash_3do(hash, iterator);
|
|
861
|
+
#endif
|
|
862
|
+
|
|
863
|
+
#ifndef RC_HASH_NO_ROM
|
|
864
|
+
case RC_CONSOLE_ARCADE:
|
|
865
|
+
return rc_hash_arcade(hash, iterator);
|
|
866
|
+
|
|
867
|
+
case RC_CONSOLE_ARDUBOY:
|
|
868
|
+
return rc_hash_arduboy(hash, iterator);
|
|
869
|
+
#endif
|
|
870
|
+
|
|
871
|
+
#ifndef RC_HASH_NO_DISC
|
|
872
|
+
case RC_CONSOLE_ATARI_JAGUAR_CD:
|
|
873
|
+
return rc_hash_jaguar_cd(hash, iterator);
|
|
874
|
+
|
|
875
|
+
case RC_CONSOLE_DREAMCAST:
|
|
876
|
+
if (rc_path_compare_extension(path, "m3u"))
|
|
877
|
+
return rc_hash_generate_from_playlist(hash, console_id, iterator);
|
|
878
|
+
|
|
879
|
+
return rc_hash_dreamcast(hash, iterator);
|
|
880
|
+
|
|
881
|
+
case RC_CONSOLE_GAMECUBE:
|
|
882
|
+
return rc_hash_gamecube(hash, iterator);
|
|
883
|
+
#endif
|
|
884
|
+
|
|
885
|
+
#ifndef RC_HASH_NO_ZIP
|
|
886
|
+
case RC_CONSOLE_MS_DOS:
|
|
887
|
+
return rc_hash_ms_dos(hash, iterator);
|
|
888
|
+
#endif
|
|
889
|
+
|
|
890
|
+
#ifndef RC_HASH_NO_DISC
|
|
891
|
+
case RC_CONSOLE_NEO_GEO_CD:
|
|
892
|
+
return rc_hash_neogeo_cd(hash, iterator);
|
|
893
|
+
#endif
|
|
894
|
+
|
|
895
|
+
#ifndef RC_HASH_NO_ROM
|
|
896
|
+
case RC_CONSOLE_NINTENDO_64:
|
|
897
|
+
return rc_hash_n64(hash, iterator);
|
|
898
|
+
#endif
|
|
899
|
+
|
|
900
|
+
#ifndef RC_HASH_NO_ENCRYPTED
|
|
901
|
+
case RC_CONSOLE_NINTENDO_3DS:
|
|
902
|
+
return rc_hash_nintendo_3ds(hash, iterator);
|
|
903
|
+
#endif
|
|
904
|
+
|
|
905
|
+
#ifndef RC_HASH_NO_ROM
|
|
906
|
+
case RC_CONSOLE_NINTENDO_DS:
|
|
907
|
+
case RC_CONSOLE_NINTENDO_DSI:
|
|
908
|
+
return rc_hash_nintendo_ds(hash, iterator);
|
|
909
|
+
#endif
|
|
910
|
+
|
|
911
|
+
#ifndef RC_HASH_NO_DISC
|
|
912
|
+
case RC_CONSOLE_PC_ENGINE_CD:
|
|
913
|
+
if (rc_path_compare_extension(path, "cue") || rc_path_compare_extension(path, "chd"))
|
|
914
|
+
return rc_hash_pce_cd(hash, iterator);
|
|
915
|
+
|
|
916
|
+
if (rc_path_compare_extension(path, "m3u"))
|
|
917
|
+
return rc_hash_generate_from_playlist(hash, console_id, iterator);
|
|
918
|
+
|
|
919
|
+
return rc_hash_buffered_file(hash, console_id, iterator);
|
|
920
|
+
|
|
921
|
+
case RC_CONSOLE_PCFX:
|
|
922
|
+
if (rc_path_compare_extension(path, "m3u"))
|
|
923
|
+
return rc_hash_generate_from_playlist(hash, console_id, iterator);
|
|
924
|
+
|
|
925
|
+
return rc_hash_pcfx_cd(hash, iterator);
|
|
926
|
+
|
|
927
|
+
case RC_CONSOLE_PLAYSTATION:
|
|
928
|
+
if (rc_path_compare_extension(path, "m3u"))
|
|
929
|
+
return rc_hash_generate_from_playlist(hash, console_id, iterator);
|
|
930
|
+
|
|
931
|
+
return rc_hash_psx(hash, iterator);
|
|
932
|
+
|
|
933
|
+
case RC_CONSOLE_PLAYSTATION_2:
|
|
934
|
+
if (rc_path_compare_extension(path, "m3u"))
|
|
935
|
+
return rc_hash_generate_from_playlist(hash, console_id, iterator);
|
|
936
|
+
|
|
937
|
+
return rc_hash_ps2(hash, iterator);
|
|
938
|
+
|
|
939
|
+
case RC_CONSOLE_PSP:
|
|
940
|
+
return rc_hash_psp(hash, iterator);
|
|
941
|
+
|
|
942
|
+
case RC_CONSOLE_SEGA_CD:
|
|
943
|
+
case RC_CONSOLE_SATURN:
|
|
944
|
+
if (rc_path_compare_extension(path, "m3u"))
|
|
945
|
+
return rc_hash_generate_from_playlist(hash, console_id, iterator);
|
|
946
|
+
|
|
947
|
+
return rc_hash_sega_cd(hash, iterator);
|
|
948
|
+
|
|
949
|
+
case RC_CONSOLE_WII:
|
|
950
|
+
return rc_hash_wii(hash, iterator);
|
|
951
|
+
#endif
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
static void rc_hash_initialize_iterator_from_path(rc_hash_iterator_t* iterator, const char* path);
|
|
956
|
+
|
|
957
|
+
static void rc_hash_iterator_append_console(struct rc_hash_iterator* iterator, uint8_t console_id) {
|
|
958
|
+
int i = 0;
|
|
959
|
+
while (iterator->consoles[i] != 0) {
|
|
960
|
+
if (iterator->consoles[i] == console_id)
|
|
961
|
+
return;
|
|
962
|
+
|
|
963
|
+
++i;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
iterator->consoles[i] = console_id;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
void rc_hash_merge_callbacks(rc_hash_iterator_t* iterator, const rc_hash_callbacks_t* callbacks)
|
|
970
|
+
{
|
|
971
|
+
if (callbacks->verbose_message)
|
|
972
|
+
iterator->callbacks.verbose_message = callbacks->verbose_message;
|
|
973
|
+
if (callbacks->error_message)
|
|
974
|
+
iterator->callbacks.verbose_message = callbacks->error_message;
|
|
975
|
+
|
|
976
|
+
if (callbacks->filereader.open)
|
|
977
|
+
memcpy(&iterator->callbacks.filereader, &callbacks->filereader, sizeof(callbacks->filereader));
|
|
978
|
+
|
|
979
|
+
#ifndef RC_HASH_NO_DISC
|
|
980
|
+
if (callbacks->cdreader.open_track)
|
|
981
|
+
memcpy(&iterator->callbacks.cdreader, &callbacks->cdreader, sizeof(callbacks->cdreader));
|
|
982
|
+
#endif
|
|
983
|
+
|
|
984
|
+
#ifndef RC_HASH_NO_ENCRYPTED
|
|
985
|
+
if (callbacks->encryption.get_3ds_cia_normal_key)
|
|
986
|
+
iterator->callbacks.encryption.get_3ds_cia_normal_key = callbacks->encryption.get_3ds_cia_normal_key;
|
|
987
|
+
if (callbacks->encryption.get_3ds_ncch_normal_keys)
|
|
988
|
+
iterator->callbacks.encryption.get_3ds_ncch_normal_keys = callbacks->encryption.get_3ds_ncch_normal_keys;
|
|
989
|
+
#endif
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
static void rc_hash_reset_iterator(rc_hash_iterator_t* iterator) {
|
|
994
|
+
memset(iterator, 0, sizeof(*iterator));
|
|
995
|
+
iterator->index = -1;
|
|
996
|
+
|
|
997
|
+
if (g_verbose_message_callback)
|
|
998
|
+
iterator->callbacks.verbose_message = rc_hash_call_g_verbose_message_callback;
|
|
999
|
+
if (g_error_message_callback)
|
|
1000
|
+
iterator->callbacks.error_message = rc_hash_call_g_error_message_callback;
|
|
1001
|
+
|
|
1002
|
+
if (g_filereader) {
|
|
1003
|
+
memcpy(&iterator->callbacks.filereader, g_filereader, sizeof(*g_filereader));
|
|
1004
|
+
} else if (!iterator->callbacks.filereader.open) {
|
|
1005
|
+
iterator->callbacks.filereader.open = filereader_open;
|
|
1006
|
+
iterator->callbacks.filereader.close = filereader_close;
|
|
1007
|
+
iterator->callbacks.filereader.seek = filereader_seek;
|
|
1008
|
+
iterator->callbacks.filereader.tell = filereader_tell;
|
|
1009
|
+
iterator->callbacks.filereader.read = filereader_read;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
#ifndef RC_HASH_NO_DISC
|
|
1013
|
+
rc_hash_reset_iterator_disc(iterator);
|
|
1014
|
+
#endif
|
|
1015
|
+
|
|
1016
|
+
#ifndef RC_HASH_NO_ENCRYPTED
|
|
1017
|
+
rc_hash_reset_iterator_encrypted(iterator);
|
|
1018
|
+
#endif
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
static void rc_hash_initialize_iterator_single(rc_hash_iterator_t* iterator, int data) {
|
|
1022
|
+
iterator->consoles[0] = (uint8_t)data;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
static void rc_hash_initialize_iterator_bin(rc_hash_iterator_t* iterator, int data) {
|
|
1026
|
+
(void)data;
|
|
1027
|
+
|
|
1028
|
+
if (iterator->buffer_size == 0) {
|
|
1029
|
+
/* raw bin file may be a CD track. if it's more than 32MB, try a CD hash. */
|
|
1030
|
+
const int64_t size = rc_file_size(iterator, iterator->path);
|
|
1031
|
+
if (size > 32 * 1024 * 1024) {
|
|
1032
|
+
iterator->consoles[0] = RC_CONSOLE_3DO; /* 4DO supports directly opening the bin file */
|
|
1033
|
+
iterator->consoles[1] = RC_CONSOLE_PLAYSTATION; /* PCSX ReARMed supports directly opening the bin file*/
|
|
1034
|
+
iterator->consoles[2] = RC_CONSOLE_PLAYSTATION_2; /* PCSX2 supports directly opening the bin file*/
|
|
1035
|
+
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* Genesis Plus GX supports directly opening the bin file*/
|
|
1036
|
+
|
|
1037
|
+
/* fallback to megadrive which just does a full hash. */
|
|
1038
|
+
iterator->consoles[4] = RC_CONSOLE_MEGA_DRIVE;
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
/* bin is associated with MegaDrive, Sega32X, Atari 2600, Watara Supervision, MegaDuck,
|
|
1044
|
+
* Fairchild Channel F, Arcadia 2001, Interton VC 4000, and Super Cassette Vision.
|
|
1045
|
+
* Since they all use the same hashing algorithm, only specify one of them */
|
|
1046
|
+
iterator->consoles[0] = RC_CONSOLE_MEGA_DRIVE;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
static void rc_hash_initialize_iterator_chd(rc_hash_iterator_t* iterator, int data) {
|
|
1050
|
+
(void)data;
|
|
1051
|
+
|
|
1052
|
+
iterator->consoles[0] = RC_CONSOLE_PLAYSTATION;
|
|
1053
|
+
iterator->consoles[1] = RC_CONSOLE_PLAYSTATION_2;
|
|
1054
|
+
iterator->consoles[2] = RC_CONSOLE_DREAMCAST;
|
|
1055
|
+
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
|
|
1056
|
+
iterator->consoles[4] = RC_CONSOLE_PSP;
|
|
1057
|
+
iterator->consoles[5] = RC_CONSOLE_PC_ENGINE_CD;
|
|
1058
|
+
iterator->consoles[6] = RC_CONSOLE_3DO;
|
|
1059
|
+
iterator->consoles[7] = RC_CONSOLE_NEO_GEO_CD;
|
|
1060
|
+
iterator->consoles[8] = RC_CONSOLE_PCFX;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
static void rc_hash_initialize_iterator_cue(rc_hash_iterator_t* iterator, int data) {
|
|
1064
|
+
(void)data;
|
|
1065
|
+
|
|
1066
|
+
iterator->consoles[0] = RC_CONSOLE_PLAYSTATION;
|
|
1067
|
+
iterator->consoles[1] = RC_CONSOLE_PLAYSTATION_2;
|
|
1068
|
+
iterator->consoles[2] = RC_CONSOLE_DREAMCAST;
|
|
1069
|
+
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
|
|
1070
|
+
iterator->consoles[4] = RC_CONSOLE_PC_ENGINE_CD;
|
|
1071
|
+
iterator->consoles[5] = RC_CONSOLE_3DO;
|
|
1072
|
+
iterator->consoles[6] = RC_CONSOLE_PCFX;
|
|
1073
|
+
iterator->consoles[7] = RC_CONSOLE_NEO_GEO_CD;
|
|
1074
|
+
iterator->consoles[8] = RC_CONSOLE_ATARI_JAGUAR_CD;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
static void rc_hash_initialize_iterator_d88(rc_hash_iterator_t* iterator, int data) {
|
|
1078
|
+
(void)data;
|
|
1079
|
+
|
|
1080
|
+
iterator->consoles[0] = RC_CONSOLE_PC8800;
|
|
1081
|
+
iterator->consoles[1] = RC_CONSOLE_SHARPX1;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
static void rc_hash_initialize_iterator_dsk(rc_hash_iterator_t* iterator, int data) {
|
|
1085
|
+
size_t size = iterator->buffer_size;
|
|
1086
|
+
if (size == 0)
|
|
1087
|
+
size = (size_t)rc_file_size(iterator, iterator->path);
|
|
1088
|
+
|
|
1089
|
+
(void)data;
|
|
1090
|
+
|
|
1091
|
+
if (size == 512 * 9 * 80) { /* 360KB */
|
|
1092
|
+
/* FAT-12 3.5" DD (512 byte sectors, 9 sectors per track, 80 tracks per side */
|
|
1093
|
+
/* FAT-12 5.25" DD double-sided (512 byte sectors, 9 sectors per track, 80 tracks per side */
|
|
1094
|
+
iterator->consoles[0] = RC_CONSOLE_MSX;
|
|
1095
|
+
}
|
|
1096
|
+
else if (size == 512 * 9 * 80 * 2) { /* 720KB */
|
|
1097
|
+
/* FAT-12 3.5" DD double-sided (512 byte sectors, 9 sectors per track, 80 tracks per side */
|
|
1098
|
+
iterator->consoles[0] = RC_CONSOLE_MSX;
|
|
1099
|
+
}
|
|
1100
|
+
else if (size == 512 * 9 * 40) { /* 180KB */
|
|
1101
|
+
/* FAT-12 5.25" DD (512 byte sectors, 9 sectors per track, 40 tracks per side */
|
|
1102
|
+
iterator->consoles[0] = RC_CONSOLE_MSX;
|
|
1103
|
+
|
|
1104
|
+
/* AMSDOS 3" - 40 tracks */
|
|
1105
|
+
iterator->consoles[1] = RC_CONSOLE_AMSTRAD_PC;
|
|
1106
|
+
}
|
|
1107
|
+
else if (size == 256 * 16 * 35) { /* 140KB */
|
|
1108
|
+
/* Apple II new format - 256 byte sectors, 16 sectors per track, 35 tracks per side */
|
|
1109
|
+
iterator->consoles[0] = RC_CONSOLE_APPLE_II;
|
|
1110
|
+
}
|
|
1111
|
+
else if (size == 256 * 13 * 35) { /* 113.75KB */
|
|
1112
|
+
/* Apple II old format - 256 byte sectors, 13 sectors per track, 35 tracks per side */
|
|
1113
|
+
iterator->consoles[0] = RC_CONSOLE_APPLE_II;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/* once a best guess has been identified, make sure the others are added as fallbacks */
|
|
1117
|
+
|
|
1118
|
+
/* check MSX first, as Apple II isn't supported by RetroArch, and RAppleWin won't use the iterator */
|
|
1119
|
+
rc_hash_iterator_append_console(iterator, RC_CONSOLE_MSX);
|
|
1120
|
+
rc_hash_iterator_append_console(iterator, RC_CONSOLE_AMSTRAD_PC);
|
|
1121
|
+
rc_hash_iterator_append_console(iterator, RC_CONSOLE_ZX_SPECTRUM);
|
|
1122
|
+
rc_hash_iterator_append_console(iterator, RC_CONSOLE_APPLE_II);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
static void rc_hash_initialize_iterator_iso(rc_hash_iterator_t* iterator, int data) {
|
|
1126
|
+
(void)data;
|
|
1127
|
+
|
|
1128
|
+
iterator->consoles[0] = RC_CONSOLE_PLAYSTATION_2;
|
|
1129
|
+
iterator->consoles[1] = RC_CONSOLE_PSP;
|
|
1130
|
+
iterator->consoles[2] = RC_CONSOLE_3DO;
|
|
1131
|
+
iterator->consoles[3] = RC_CONSOLE_SEGA_CD; /* ASSERT: handles both Sega CD and Saturn */
|
|
1132
|
+
iterator->consoles[4] = RC_CONSOLE_GAMECUBE;
|
|
1133
|
+
iterator->consoles[5] = RC_CONSOLE_WII;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
static void rc_hash_initialize_iterator_m3u(rc_hash_iterator_t* iterator, int data) {
|
|
1137
|
+
const char* first_file_path;
|
|
1138
|
+
|
|
1139
|
+
(void)data;
|
|
1140
|
+
|
|
1141
|
+
/* temporarily set the iterator path to the m3u file so we can extract the
|
|
1142
|
+
* path of the first disc. rc_hash_get_first_item_from_playlist will return
|
|
1143
|
+
* an allocated string or NULL, so rc_hash_destroy_iterator won't get tripped
|
|
1144
|
+
* up by the non-allocted value we're about to assign.
|
|
1145
|
+
*/
|
|
1146
|
+
first_file_path = rc_hash_get_first_item_from_playlist(iterator);
|
|
1147
|
+
if (!first_file_path) /* did not find a disc */
|
|
1148
|
+
return;
|
|
1149
|
+
|
|
1150
|
+
/* release the m3u path and replace with the first file path */
|
|
1151
|
+
free((void*)iterator->path);
|
|
1152
|
+
iterator->path = first_file_path; /* assert: already malloc'd; don't need to strdup */
|
|
1153
|
+
|
|
1154
|
+
iterator->buffer = NULL; /* ignore buffer; assume it's the m3u contents */
|
|
1155
|
+
|
|
1156
|
+
rc_hash_initialize_iterator_from_path(iterator, iterator->path);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
static void rc_hash_initialize_iterator_nib(rc_hash_iterator_t* iterator, int data) {
|
|
1160
|
+
(void)data;
|
|
1161
|
+
|
|
1162
|
+
iterator->consoles[0] = RC_CONSOLE_APPLE_II;
|
|
1163
|
+
iterator->consoles[1] = RC_CONSOLE_COMMODORE_64;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
static void rc_hash_initialize_iterator_rom(rc_hash_iterator_t* iterator, int data) {
|
|
1167
|
+
(void)data;
|
|
1168
|
+
|
|
1169
|
+
/* rom is associated with MSX, Thomson TO-8, and Fairchild Channel F.
|
|
1170
|
+
* Since they all use the same hashing algorithm, only specify one of them */
|
|
1171
|
+
iterator->consoles[0] = RC_CONSOLE_MSX;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
static void rc_hash_initialize_iterator_tap(rc_hash_iterator_t* iterator, int data) {
|
|
1175
|
+
(void)data;
|
|
1176
|
+
|
|
1177
|
+
/* also Oric and ZX Spectrum, but all are full file hashes */
|
|
1178
|
+
iterator->consoles[0] = RC_CONSOLE_COMMODORE_64;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
static const rc_hash_iterator_ext_handler_entry_t rc_hash_iterator_ext_handlers[] = {
|
|
1182
|
+
{ "2d", rc_hash_initialize_iterator_single, RC_CONSOLE_SHARPX1 },
|
|
1183
|
+
{ "3ds", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO_3DS },
|
|
1184
|
+
{ "3dsx", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO_3DS },
|
|
1185
|
+
{ "7z", rc_hash_initialize_iterator_single, RC_CONSOLE_ARCADE },
|
|
1186
|
+
{ "83g", rc_hash_initialize_iterator_single, RC_CONSOLE_TI83 }, /* http://tibasicdev.wikidot.com/file-extensions */
|
|
1187
|
+
{ "83p", rc_hash_initialize_iterator_single, RC_CONSOLE_TI83 },
|
|
1188
|
+
{ "a26", rc_hash_initialize_iterator_single, RC_CONSOLE_ATARI_2600 },
|
|
1189
|
+
{ "a78", rc_hash_initialize_iterator_single, RC_CONSOLE_ATARI_7800 },
|
|
1190
|
+
{ "app", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO_3DS },
|
|
1191
|
+
{ "arduboy", rc_hash_initialize_iterator_single, RC_CONSOLE_ARDUBOY },
|
|
1192
|
+
{ "axf", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO_3DS },
|
|
1193
|
+
{ "bin", rc_hash_initialize_iterator_bin, 0 },
|
|
1194
|
+
{ "bs", rc_hash_initialize_iterator_single, RC_CONSOLE_SUPER_NINTENDO },
|
|
1195
|
+
{ "cart", rc_hash_initialize_iterator_single, RC_CONSOLE_SUPER_CASSETTEVISION },
|
|
1196
|
+
{ "cas", rc_hash_initialize_iterator_single, RC_CONSOLE_MSX },
|
|
1197
|
+
{ "cci", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO_3DS },
|
|
1198
|
+
{ "chd", rc_hash_initialize_iterator_chd, 0 },
|
|
1199
|
+
{ "chf", rc_hash_initialize_iterator_single, RC_CONSOLE_FAIRCHILD_CHANNEL_F },
|
|
1200
|
+
{ "cia", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO_3DS },
|
|
1201
|
+
{ "col", rc_hash_initialize_iterator_single, RC_CONSOLE_COLECOVISION },
|
|
1202
|
+
{ "csw", rc_hash_initialize_iterator_single, RC_CONSOLE_ZX_SPECTRUM },
|
|
1203
|
+
{ "cue", rc_hash_initialize_iterator_cue, 0 },
|
|
1204
|
+
{ "cxi", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO_3DS },
|
|
1205
|
+
{ "d64", rc_hash_initialize_iterator_single, RC_CONSOLE_COMMODORE_64 },
|
|
1206
|
+
{ "d88", rc_hash_initialize_iterator_d88, 0 },
|
|
1207
|
+
{ "dosz", rc_hash_initialize_iterator_single, RC_CONSOLE_MS_DOS },
|
|
1208
|
+
{ "dsk", rc_hash_initialize_iterator_dsk, 0 },
|
|
1209
|
+
{ "elf", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO_3DS },
|
|
1210
|
+
{ "fd", rc_hash_initialize_iterator_single, RC_CONSOLE_THOMSONTO8 },
|
|
1211
|
+
{ "fds", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO },
|
|
1212
|
+
{ "fig", rc_hash_initialize_iterator_single, RC_CONSOLE_SUPER_NINTENDO },
|
|
1213
|
+
{ "gb", rc_hash_initialize_iterator_single, RC_CONSOLE_GAMEBOY },
|
|
1214
|
+
{ "gba", rc_hash_initialize_iterator_single, RC_CONSOLE_GAMEBOY_ADVANCE },
|
|
1215
|
+
{ "gbc", rc_hash_initialize_iterator_single, RC_CONSOLE_GAMEBOY_COLOR },
|
|
1216
|
+
{ "gdi", rc_hash_initialize_iterator_single, RC_CONSOLE_DREAMCAST },
|
|
1217
|
+
{ "gg", rc_hash_initialize_iterator_single, RC_CONSOLE_GAME_GEAR },
|
|
1218
|
+
{ "hex", rc_hash_initialize_iterator_single, RC_CONSOLE_ARDUBOY },
|
|
1219
|
+
{ "iso", rc_hash_initialize_iterator_iso, 0 },
|
|
1220
|
+
{ "jag", rc_hash_initialize_iterator_single, RC_CONSOLE_ATARI_JAGUAR },
|
|
1221
|
+
{ "k7", rc_hash_initialize_iterator_single, RC_CONSOLE_THOMSONTO8 }, /* tape */
|
|
1222
|
+
{ "lnx", rc_hash_initialize_iterator_single, RC_CONSOLE_ATARI_LYNX },
|
|
1223
|
+
{ "m3u", rc_hash_initialize_iterator_m3u, 0 },
|
|
1224
|
+
{ "m5", rc_hash_initialize_iterator_single, RC_CONSOLE_THOMSONTO8 }, /* cartridge */
|
|
1225
|
+
{ "m7", rc_hash_initialize_iterator_single, RC_CONSOLE_THOMSONTO8 }, /* cartridge */
|
|
1226
|
+
{ "md", rc_hash_initialize_iterator_single, RC_CONSOLE_MEGA_DRIVE },
|
|
1227
|
+
{ "min", rc_hash_initialize_iterator_single, RC_CONSOLE_POKEMON_MINI },
|
|
1228
|
+
{ "mx1", rc_hash_initialize_iterator_single, RC_CONSOLE_MSX },
|
|
1229
|
+
{ "mx2", rc_hash_initialize_iterator_single, RC_CONSOLE_MSX },
|
|
1230
|
+
{ "n64", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO_64 },
|
|
1231
|
+
{ "ndd", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO_64 },
|
|
1232
|
+
{ "nds", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO_DS }, /* handles both DS and DSi */
|
|
1233
|
+
{ "nes", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO },
|
|
1234
|
+
{ "ngc", rc_hash_initialize_iterator_single, RC_CONSOLE_NEOGEO_POCKET },
|
|
1235
|
+
{ "nib", rc_hash_initialize_iterator_nib, 0 },
|
|
1236
|
+
{ "pbp", rc_hash_initialize_iterator_single, RC_CONSOLE_PSP },
|
|
1237
|
+
{ "pce", rc_hash_initialize_iterator_single, RC_CONSOLE_PC_ENGINE },
|
|
1238
|
+
{ "pgm", rc_hash_initialize_iterator_single, RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER },
|
|
1239
|
+
{ "pzx", rc_hash_initialize_iterator_single, RC_CONSOLE_ZX_SPECTRUM },
|
|
1240
|
+
{ "ri", rc_hash_initialize_iterator_single, RC_CONSOLE_MSX },
|
|
1241
|
+
{ "rom", rc_hash_initialize_iterator_rom, 0 },
|
|
1242
|
+
{ "sap", rc_hash_initialize_iterator_single, RC_CONSOLE_THOMSONTO8 }, /* disk */
|
|
1243
|
+
{ "scl", rc_hash_initialize_iterator_single, RC_CONSOLE_ZX_SPECTRUM },
|
|
1244
|
+
{ "sfc", rc_hash_initialize_iterator_single, RC_CONSOLE_SUPER_NINTENDO },
|
|
1245
|
+
{ "sg", rc_hash_initialize_iterator_single, RC_CONSOLE_SG1000 },
|
|
1246
|
+
{ "sgx", rc_hash_initialize_iterator_single, RC_CONSOLE_PC_ENGINE },
|
|
1247
|
+
{ "smc", rc_hash_initialize_iterator_single, RC_CONSOLE_SUPER_NINTENDO },
|
|
1248
|
+
{ "sv", rc_hash_initialize_iterator_single, RC_CONSOLE_SUPERVISION },
|
|
1249
|
+
{ "swc", rc_hash_initialize_iterator_single, RC_CONSOLE_SUPER_NINTENDO },
|
|
1250
|
+
{ "tap", rc_hash_initialize_iterator_tap, 0 },
|
|
1251
|
+
{ "tic", rc_hash_initialize_iterator_single, RC_CONSOLE_TIC80 },
|
|
1252
|
+
{ "trd", rc_hash_initialize_iterator_single, RC_CONSOLE_ZX_SPECTRUM },
|
|
1253
|
+
{ "tvc", rc_hash_initialize_iterator_single, RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER },
|
|
1254
|
+
{ "tzx", rc_hash_initialize_iterator_single, RC_CONSOLE_ZX_SPECTRUM },
|
|
1255
|
+
{ "uze", rc_hash_initialize_iterator_single, RC_CONSOLE_UZEBOX },
|
|
1256
|
+
{ "v64", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO_64 },
|
|
1257
|
+
{ "vb", rc_hash_initialize_iterator_single, RC_CONSOLE_VIRTUAL_BOY },
|
|
1258
|
+
{ "wad", rc_hash_initialize_iterator_single, RC_CONSOLE_WII },
|
|
1259
|
+
{ "wasm", rc_hash_initialize_iterator_single, RC_CONSOLE_WASM4 },
|
|
1260
|
+
{ "woz", rc_hash_initialize_iterator_single, RC_CONSOLE_APPLE_II },
|
|
1261
|
+
{ "wsc", rc_hash_initialize_iterator_single, RC_CONSOLE_WONDERSWAN },
|
|
1262
|
+
{ "z64", rc_hash_initialize_iterator_single, RC_CONSOLE_NINTENDO_64 },
|
|
1263
|
+
{ "zip", rc_hash_initialize_iterator_single, RC_CONSOLE_ARCADE }
|
|
1264
|
+
};
|
|
1265
|
+
|
|
1266
|
+
const rc_hash_iterator_ext_handler_entry_t* rc_hash_get_iterator_ext_handlers(size_t* num_handlers) {
|
|
1267
|
+
*num_handlers = sizeof(rc_hash_iterator_ext_handlers) / sizeof(rc_hash_iterator_ext_handlers[0]);
|
|
1268
|
+
return rc_hash_iterator_ext_handlers;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
static int rc_hash_iterator_find_handler(const void* left, const void* right) {
|
|
1272
|
+
const rc_hash_iterator_ext_handler_entry_t* left_handler =
|
|
1273
|
+
(const rc_hash_iterator_ext_handler_entry_t*)left;
|
|
1274
|
+
const rc_hash_iterator_ext_handler_entry_t* right_handler =
|
|
1275
|
+
(const rc_hash_iterator_ext_handler_entry_t*)right;
|
|
1276
|
+
|
|
1277
|
+
return strcmp(left_handler->ext, right_handler->ext);
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
static void rc_hash_initialize_iterator_from_path(rc_hash_iterator_t* iterator, const char* path) {
|
|
1281
|
+
size_t num_handlers;
|
|
1282
|
+
const rc_hash_iterator_ext_handler_entry_t* handlers = rc_hash_get_iterator_ext_handlers(&num_handlers);
|
|
1283
|
+
const rc_hash_iterator_ext_handler_entry_t* handler;
|
|
1284
|
+
rc_hash_iterator_ext_handler_entry_t search;
|
|
1285
|
+
const char* ext = rc_path_get_extension(path);
|
|
1286
|
+
size_t index;
|
|
1287
|
+
|
|
1288
|
+
/* lowercase the extension as we copy it into the search object */
|
|
1289
|
+
memset(&search, 0, sizeof(search));
|
|
1290
|
+
for (index = 0; index < sizeof(search.ext) - 1; ++index) {
|
|
1291
|
+
const int c = (int)ext[index];
|
|
1292
|
+
if (!c)
|
|
1293
|
+
break;
|
|
1294
|
+
|
|
1295
|
+
search.ext[index] = tolower(c);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
/* find the handler for the extension */
|
|
1299
|
+
handler = (const rc_hash_iterator_ext_handler_entry_t*)
|
|
1300
|
+
bsearch(&search, handlers, num_handlers, sizeof(*handler), rc_hash_iterator_find_handler);
|
|
1301
|
+
if (handler) {
|
|
1302
|
+
handler->handler(iterator, handler->data);
|
|
1303
|
+
|
|
1304
|
+
if (iterator->callbacks.verbose_message) {
|
|
1305
|
+
int count = 0;
|
|
1306
|
+
while (iterator->consoles[count])
|
|
1307
|
+
++count;
|
|
1308
|
+
|
|
1309
|
+
rc_hash_iterator_verbose_formatted(iterator, "Found %d potential consoles for %s file extension", count, ext);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
else {
|
|
1313
|
+
rc_hash_iterator_error_formatted(iterator, "No console mapping specified for %s file extension - trying full file hash", ext);
|
|
1314
|
+
|
|
1315
|
+
/* if we didn't match the extension, default to something that does a whole file hash */
|
|
1316
|
+
if (!iterator->consoles[0])
|
|
1317
|
+
iterator->consoles[0] = RC_CONSOLE_GAMEBOY;
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
void rc_hash_initialize_iterator(rc_hash_iterator_t* iterator, const char* path, const uint8_t* buffer, size_t buffer_size)
|
|
1322
|
+
{
|
|
1323
|
+
rc_hash_reset_iterator(iterator);
|
|
1324
|
+
iterator->buffer = buffer;
|
|
1325
|
+
iterator->buffer_size = buffer_size;
|
|
1326
|
+
|
|
1327
|
+
if (path)
|
|
1328
|
+
iterator->path = strdup(path);
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
void rc_hash_destroy_iterator(rc_hash_iterator_t* iterator) {
|
|
1332
|
+
if (iterator->path) {
|
|
1333
|
+
free((void*)iterator->path);
|
|
1334
|
+
iterator->path = NULL;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
iterator->buffer = NULL;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
int rc_hash_iterate(char hash[33], rc_hash_iterator_t* iterator) {
|
|
1341
|
+
int next_console;
|
|
1342
|
+
int result = 0;
|
|
1343
|
+
|
|
1344
|
+
if (iterator->index == -1) {
|
|
1345
|
+
rc_hash_initialize_iterator_from_path(iterator, iterator->path);
|
|
1346
|
+
iterator->index = 0;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
do {
|
|
1350
|
+
next_console = iterator->consoles[iterator->index];
|
|
1351
|
+
if (next_console == 0) {
|
|
1352
|
+
hash[0] = '\0';
|
|
1353
|
+
break;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
++iterator->index;
|
|
1357
|
+
|
|
1358
|
+
rc_hash_iterator_verbose_formatted(iterator, "Trying console %d", next_console);
|
|
1359
|
+
|
|
1360
|
+
result = rc_hash_generate(hash, next_console, iterator);
|
|
1361
|
+
} while (!result);
|
|
1362
|
+
|
|
1363
|
+
return result;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
int rc_hash_generate(char hash[33], uint32_t console_id, const rc_hash_iterator_t* iterator) {
|
|
1367
|
+
if (iterator->buffer)
|
|
1368
|
+
return rc_hash_from_buffer(hash, console_id, iterator);
|
|
1369
|
+
|
|
1370
|
+
return rc_hash_from_file(hash, console_id, iterator);
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
int rc_hash_generate_from_buffer(char hash[33], uint32_t console_id, const uint8_t* buffer, size_t buffer_size) {
|
|
1374
|
+
rc_hash_iterator_t iterator;
|
|
1375
|
+
int result;
|
|
1376
|
+
|
|
1377
|
+
rc_hash_reset_iterator(&iterator);
|
|
1378
|
+
iterator.buffer = buffer;
|
|
1379
|
+
iterator.buffer_size = buffer_size;
|
|
1380
|
+
|
|
1381
|
+
result = rc_hash_from_buffer(hash, console_id, &iterator);
|
|
1382
|
+
|
|
1383
|
+
rc_hash_destroy_iterator(&iterator);
|
|
1384
|
+
|
|
1385
|
+
return result;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
int rc_hash_generate_from_file(char hash[33], uint32_t console_id, const char* path){
|
|
1389
|
+
rc_hash_iterator_t iterator;
|
|
1390
|
+
int result;
|
|
1391
|
+
|
|
1392
|
+
rc_hash_reset_iterator(&iterator);
|
|
1393
|
+
iterator.path = path;
|
|
1394
|
+
|
|
1395
|
+
result = rc_hash_from_file(hash, console_id, &iterator);
|
|
1396
|
+
|
|
1397
|
+
iterator.path = NULL; /* prevent free. we didn't strdup */
|
|
1398
|
+
|
|
1399
|
+
rc_hash_destroy_iterator(&iterator);
|
|
1400
|
+
|
|
1401
|
+
return result;
|
|
1402
|
+
}
|