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,838 @@
|
|
|
1
|
+
#include "rc_hash_internal.h"
|
|
2
|
+
|
|
3
|
+
#include "../rc_compat.h"
|
|
4
|
+
|
|
5
|
+
#include <ctype.h>
|
|
6
|
+
#include <string.h>
|
|
7
|
+
#include <stdlib.h>
|
|
8
|
+
|
|
9
|
+
static int cdreader_get_sector(uint8_t header[16])
|
|
10
|
+
{
|
|
11
|
+
int minutes = (header[12] >> 4) * 10 + (header[12] & 0x0F);
|
|
12
|
+
int seconds = (header[13] >> 4) * 10 + (header[13] & 0x0F);
|
|
13
|
+
int frames = (header[14] >> 4) * 10 + (header[14] & 0x0F);
|
|
14
|
+
|
|
15
|
+
/* convert the MSF value to a sector index, and subtract 150 (2 seconds) per:
|
|
16
|
+
* For data and mixed mode media (those conforming to ISO/IEC 10149), logical block address
|
|
17
|
+
* zero shall be assigned to the block at MSF address 00/02/00 */
|
|
18
|
+
return ((minutes * 60) + seconds) * 75 + frames - 150;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static void cdreader_determine_sector_size(rc_hash_cdrom_track_t* cdrom)
|
|
22
|
+
{
|
|
23
|
+
/* Attempt to determine the sector and header sizes. The CUE file may be lying.
|
|
24
|
+
* Look for the sync pattern using each of the supported sector sizes.
|
|
25
|
+
* Then check for the presence of "CD001", which is gauranteed to be in either the
|
|
26
|
+
* boot record or primary volume descriptor, one of which is always at sector 16.
|
|
27
|
+
*/
|
|
28
|
+
const uint8_t sync_pattern[] = {
|
|
29
|
+
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
uint8_t header[32];
|
|
33
|
+
const int64_t toc_sector = 16 + cdrom->track_pregap_sectors;
|
|
34
|
+
|
|
35
|
+
cdrom->sector_size = 0;
|
|
36
|
+
cdrom->sector_header_size = 0;
|
|
37
|
+
cdrom->raw_data_size = 2048;
|
|
38
|
+
|
|
39
|
+
cdrom->file_reader->seek(cdrom->file_handle, toc_sector * 2352 + cdrom->file_track_offset, SEEK_SET);
|
|
40
|
+
if (cdrom->file_reader->read(cdrom->file_handle, header, sizeof(header)) < sizeof(header))
|
|
41
|
+
return;
|
|
42
|
+
|
|
43
|
+
if (memcmp(header, sync_pattern, 12) == 0)
|
|
44
|
+
{
|
|
45
|
+
cdrom->sector_size = 2352;
|
|
46
|
+
|
|
47
|
+
if (memcmp(&header[25], "CD001", 5) == 0)
|
|
48
|
+
cdrom->sector_header_size = 24;
|
|
49
|
+
else
|
|
50
|
+
cdrom->sector_header_size = 16;
|
|
51
|
+
|
|
52
|
+
cdrom->track_first_sector = cdreader_get_sector(header) - (int)toc_sector;
|
|
53
|
+
}
|
|
54
|
+
else
|
|
55
|
+
{
|
|
56
|
+
cdrom->file_reader->seek(cdrom->file_handle, toc_sector * 2336 + cdrom->file_track_offset, SEEK_SET);
|
|
57
|
+
cdrom->file_reader->read(cdrom->file_handle, header, sizeof(header));
|
|
58
|
+
|
|
59
|
+
if (memcmp(header, sync_pattern, 12) == 0)
|
|
60
|
+
{
|
|
61
|
+
cdrom->sector_size = 2336;
|
|
62
|
+
|
|
63
|
+
if (memcmp(&header[25], "CD001", 5) == 0)
|
|
64
|
+
cdrom->sector_header_size = 24;
|
|
65
|
+
else
|
|
66
|
+
cdrom->sector_header_size = 16;
|
|
67
|
+
|
|
68
|
+
cdrom->track_first_sector = cdreader_get_sector(header) - (int)toc_sector;
|
|
69
|
+
}
|
|
70
|
+
else
|
|
71
|
+
{
|
|
72
|
+
cdrom->file_reader->seek(cdrom->file_handle, toc_sector * 2048 + cdrom->file_track_offset, SEEK_SET);
|
|
73
|
+
cdrom->file_reader->read(cdrom->file_handle, header, sizeof(header));
|
|
74
|
+
|
|
75
|
+
if (memcmp(&header[1], "CD001", 5) == 0)
|
|
76
|
+
{
|
|
77
|
+
cdrom->sector_size = 2048;
|
|
78
|
+
cdrom->sector_header_size = 0;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static void* cdreader_open_bin_track(const char* path, uint32_t track, const rc_hash_iterator_t* iterator)
|
|
85
|
+
{
|
|
86
|
+
void* file_handle;
|
|
87
|
+
rc_hash_cdrom_track_t* cdrom;
|
|
88
|
+
|
|
89
|
+
if (track > 1) {
|
|
90
|
+
rc_hash_iterator_verbose(iterator, "Cannot locate secondary tracks without a cue sheet");
|
|
91
|
+
return NULL;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
file_handle = iterator->callbacks.filereader.open(path);
|
|
95
|
+
if (!file_handle)
|
|
96
|
+
return NULL;
|
|
97
|
+
|
|
98
|
+
cdrom = (rc_hash_cdrom_track_t*)calloc(1, sizeof(*cdrom));
|
|
99
|
+
if (!cdrom)
|
|
100
|
+
return NULL;
|
|
101
|
+
cdrom->file_reader = &iterator->callbacks.filereader;
|
|
102
|
+
cdrom->file_handle = file_handle;
|
|
103
|
+
#ifndef NDEBUG
|
|
104
|
+
cdrom->track_id = track;
|
|
105
|
+
#endif
|
|
106
|
+
|
|
107
|
+
cdreader_determine_sector_size(cdrom);
|
|
108
|
+
|
|
109
|
+
if (cdrom->sector_size == 0)
|
|
110
|
+
{
|
|
111
|
+
int64_t size;
|
|
112
|
+
|
|
113
|
+
iterator->callbacks.filereader.seek(file_handle, 0, SEEK_END);
|
|
114
|
+
size = iterator->callbacks.filereader.tell(file_handle);
|
|
115
|
+
|
|
116
|
+
if ((size % 2352) == 0)
|
|
117
|
+
{
|
|
118
|
+
/* raw tracks use all 2352 bytes and have a 24 byte header */
|
|
119
|
+
cdrom->sector_size = 2352;
|
|
120
|
+
cdrom->sector_header_size = 24;
|
|
121
|
+
}
|
|
122
|
+
else if ((size % 2048) == 0)
|
|
123
|
+
{
|
|
124
|
+
/* cooked tracks eliminate all header/footer data */
|
|
125
|
+
cdrom->sector_size = 2048;
|
|
126
|
+
cdrom->sector_header_size = 0;
|
|
127
|
+
}
|
|
128
|
+
else if ((size % 2336) == 0)
|
|
129
|
+
{
|
|
130
|
+
/* MODE 2 format without 16-byte sync data */
|
|
131
|
+
cdrom->sector_size = 2336;
|
|
132
|
+
cdrom->sector_header_size = 8;
|
|
133
|
+
}
|
|
134
|
+
else
|
|
135
|
+
{
|
|
136
|
+
if (iterator->callbacks.filereader.close)
|
|
137
|
+
iterator->callbacks.filereader.close(file_handle);
|
|
138
|
+
free(cdrom);
|
|
139
|
+
|
|
140
|
+
rc_hash_iterator_verbose(iterator, "Could not determine sector size");
|
|
141
|
+
|
|
142
|
+
return NULL;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return cdrom;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
static int cdreader_open_bin(rc_hash_cdrom_track_t* cdrom, const char* path, const char* mode)
|
|
150
|
+
{
|
|
151
|
+
cdrom->file_handle = cdrom->file_reader->open(path);
|
|
152
|
+
if (!cdrom->file_handle)
|
|
153
|
+
return 0;
|
|
154
|
+
|
|
155
|
+
/* determine sector size */
|
|
156
|
+
cdreader_determine_sector_size(cdrom);
|
|
157
|
+
|
|
158
|
+
/* could not determine, which means we'll probably have more issues later
|
|
159
|
+
* but use the CUE provided information anyway
|
|
160
|
+
*/
|
|
161
|
+
if (cdrom->sector_size == 0)
|
|
162
|
+
{
|
|
163
|
+
/* All of these modes have 2048 byte payloads. In MODE1/2352 and MODE2/2352
|
|
164
|
+
* modes, the mode can actually be specified per sector to change the payload
|
|
165
|
+
* size, but that reduces the ability to recover from errors when the disc
|
|
166
|
+
* is damaged, so it's seldomly used, and when it is, it's mostly for audio
|
|
167
|
+
* or video data where a blip or two probably won't be noticed by the user.
|
|
168
|
+
* So, while we techincally support all of the following modes, we only do
|
|
169
|
+
* so with 2048 byte payloads.
|
|
170
|
+
* http://totalsonicmastering.com/cuesheetsyntax.htm
|
|
171
|
+
* MODE1/2048 ? CDROM Mode1 Data (cooked) [no header, no footer]
|
|
172
|
+
* MODE1/2352 ? CDROM Mode1 Data (raw) [16 byte header, 288 byte footer]
|
|
173
|
+
* MODE2/2336 ? CDROM-XA Mode2 Data [8 byte header, 280 byte footer]
|
|
174
|
+
* MODE2/2352 ? CDROM-XA Mode2 Data [24 byte header, 280 byte footer]
|
|
175
|
+
*/
|
|
176
|
+
if (memcmp(mode, "MODE2/2352", 10) == 0)
|
|
177
|
+
{
|
|
178
|
+
cdrom->sector_size = 2352;
|
|
179
|
+
cdrom->sector_header_size = 24;
|
|
180
|
+
}
|
|
181
|
+
else if (memcmp(mode, "MODE1/2048", 10) == 0)
|
|
182
|
+
{
|
|
183
|
+
cdrom->sector_size = 2048;
|
|
184
|
+
cdrom->sector_header_size = 0;
|
|
185
|
+
}
|
|
186
|
+
else if (memcmp(mode, "MODE2/2336", 10) == 0)
|
|
187
|
+
{
|
|
188
|
+
cdrom->sector_size = 2336;
|
|
189
|
+
cdrom->sector_header_size = 8;
|
|
190
|
+
}
|
|
191
|
+
else if (memcmp(mode, "MODE1/2352", 10) == 0)
|
|
192
|
+
{
|
|
193
|
+
cdrom->sector_size = 2352;
|
|
194
|
+
cdrom->sector_header_size = 16;
|
|
195
|
+
}
|
|
196
|
+
else if (memcmp(mode, "AUDIO", 5) == 0)
|
|
197
|
+
{
|
|
198
|
+
cdrom->sector_size = 2352;
|
|
199
|
+
cdrom->sector_header_size = 0;
|
|
200
|
+
cdrom->raw_data_size = 2352; /* no header or footer data on audio tracks */
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return (cdrom->sector_size != 0);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
static char* cdreader_get_bin_path(const char* cue_path, const char* bin_name, const rc_hash_iterator_t* iterator)
|
|
208
|
+
{
|
|
209
|
+
const char* filename = rc_path_get_filename(cue_path);
|
|
210
|
+
const size_t bin_name_len = strlen(bin_name);
|
|
211
|
+
const size_t cue_path_len = filename - cue_path;
|
|
212
|
+
const size_t needed = cue_path_len + bin_name_len + 1;
|
|
213
|
+
|
|
214
|
+
char* bin_filename = (char*)malloc(needed);
|
|
215
|
+
if (!bin_filename)
|
|
216
|
+
{
|
|
217
|
+
rc_hash_iterator_error_formatted(iterator, "Failed to allocate %u bytes", (unsigned)needed);
|
|
218
|
+
}
|
|
219
|
+
else
|
|
220
|
+
{
|
|
221
|
+
memcpy(bin_filename, cue_path, cue_path_len);
|
|
222
|
+
memcpy(bin_filename + cue_path_len, bin_name, bin_name_len + 1);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return bin_filename;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
static int64_t cdreader_get_bin_size(const char* cue_path, const char* bin_name, const rc_hash_iterator_t* iterator)
|
|
229
|
+
{
|
|
230
|
+
int64_t size = 0;
|
|
231
|
+
char* bin_filename = cdreader_get_bin_path(cue_path, bin_name, iterator);
|
|
232
|
+
if (bin_filename)
|
|
233
|
+
{
|
|
234
|
+
void* handle = iterator->callbacks.filereader.open(bin_filename);
|
|
235
|
+
if (handle)
|
|
236
|
+
{
|
|
237
|
+
iterator->callbacks.filereader.seek(handle, 0, SEEK_END);
|
|
238
|
+
size = iterator->callbacks.filereader.tell(handle);
|
|
239
|
+
|
|
240
|
+
if (iterator->callbacks.filereader.close)
|
|
241
|
+
iterator->callbacks.filereader.close(handle);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
free(bin_filename);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return size;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static void* cdreader_open_cue_track(const char* path, uint32_t track, const rc_hash_iterator_t* iterator)
|
|
251
|
+
{
|
|
252
|
+
void* cue_handle;
|
|
253
|
+
int64_t cue_offset = 0;
|
|
254
|
+
char buffer[1024];
|
|
255
|
+
char* bin_filename = NULL;
|
|
256
|
+
char *ptr, *ptr2, *end;
|
|
257
|
+
int done = 0;
|
|
258
|
+
int session = 1;
|
|
259
|
+
size_t num_read = 0;
|
|
260
|
+
rc_hash_cdrom_track_t* cdrom = NULL;
|
|
261
|
+
|
|
262
|
+
struct track_t
|
|
263
|
+
{
|
|
264
|
+
uint32_t id;
|
|
265
|
+
int sector_size;
|
|
266
|
+
int sector_count;
|
|
267
|
+
int first_sector;
|
|
268
|
+
int pregap_sectors;
|
|
269
|
+
int is_data;
|
|
270
|
+
int file_track_offset;
|
|
271
|
+
int file_first_sector;
|
|
272
|
+
char mode[16];
|
|
273
|
+
char filename[256];
|
|
274
|
+
} current_track, previous_track, largest_track;
|
|
275
|
+
|
|
276
|
+
cue_handle = iterator->callbacks.filereader.open(path);
|
|
277
|
+
if (!cue_handle)
|
|
278
|
+
return NULL;
|
|
279
|
+
|
|
280
|
+
memset(¤t_track, 0, sizeof(current_track));
|
|
281
|
+
memset(&previous_track, 0, sizeof(previous_track));
|
|
282
|
+
memset(&largest_track, 0, sizeof(largest_track));
|
|
283
|
+
|
|
284
|
+
do
|
|
285
|
+
{
|
|
286
|
+
num_read = iterator->callbacks.filereader.read(cue_handle, buffer, sizeof(buffer) - 1);
|
|
287
|
+
if (num_read == 0)
|
|
288
|
+
break;
|
|
289
|
+
|
|
290
|
+
buffer[num_read] = 0;
|
|
291
|
+
if (num_read == sizeof(buffer) - 1)
|
|
292
|
+
end = buffer + sizeof(buffer) * 3 / 4;
|
|
293
|
+
else
|
|
294
|
+
end = buffer + num_read;
|
|
295
|
+
|
|
296
|
+
for (ptr = buffer; ptr < end; ++ptr)
|
|
297
|
+
{
|
|
298
|
+
while (*ptr == ' ')
|
|
299
|
+
++ptr;
|
|
300
|
+
|
|
301
|
+
if (strncasecmp(ptr, "INDEX ", 6) == 0)
|
|
302
|
+
{
|
|
303
|
+
int m = 0, s = 0, f = 0;
|
|
304
|
+
int index;
|
|
305
|
+
int sector_offset;
|
|
306
|
+
|
|
307
|
+
ptr += 6;
|
|
308
|
+
index = atoi(ptr);
|
|
309
|
+
|
|
310
|
+
while (*ptr != ' ' && *ptr != '\n')
|
|
311
|
+
++ptr;
|
|
312
|
+
while (*ptr == ' ')
|
|
313
|
+
++ptr;
|
|
314
|
+
|
|
315
|
+
/* convert mm:ss:ff to sector count */
|
|
316
|
+
sscanf_s(ptr, "%d:%d:%d", &m, &s, &f);
|
|
317
|
+
sector_offset = ((m * 60) + s) * 75 + f;
|
|
318
|
+
|
|
319
|
+
if (current_track.first_sector == -1)
|
|
320
|
+
{
|
|
321
|
+
current_track.first_sector = sector_offset;
|
|
322
|
+
if (strcmp(current_track.filename, previous_track.filename) == 0)
|
|
323
|
+
{
|
|
324
|
+
previous_track.sector_count = current_track.first_sector - previous_track.first_sector;
|
|
325
|
+
current_track.file_track_offset += previous_track.sector_count * previous_track.sector_size;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* if looking for the largest data track, determine previous track size */
|
|
329
|
+
if (track == RC_HASH_CDTRACK_LARGEST && previous_track.sector_count > largest_track.sector_count &&
|
|
330
|
+
previous_track.is_data)
|
|
331
|
+
{
|
|
332
|
+
memcpy(&largest_track, &previous_track, sizeof(largest_track));
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (index == 1)
|
|
337
|
+
{
|
|
338
|
+
current_track.pregap_sectors = (sector_offset - current_track.first_sector);
|
|
339
|
+
|
|
340
|
+
{
|
|
341
|
+
char* scan = current_track.mode;
|
|
342
|
+
while (*scan && !isspace((unsigned char)*scan))
|
|
343
|
+
++scan;
|
|
344
|
+
*scan = '\0';
|
|
345
|
+
|
|
346
|
+
/* it's undesirable to truncate offset to 32-bits, but %lld isn't defined in c89. */
|
|
347
|
+
rc_hash_iterator_verbose_formatted(iterator, "Found %s track %d (first sector %d, sector size %d, %d pregap sectors)",
|
|
348
|
+
current_track.mode, current_track.id, current_track.first_sector, current_track.sector_size, current_track.pregap_sectors);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (current_track.id == track)
|
|
352
|
+
{
|
|
353
|
+
done = 1;
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (track == RC_HASH_CDTRACK_FIRST_DATA && current_track.is_data)
|
|
358
|
+
{
|
|
359
|
+
track = current_track.id;
|
|
360
|
+
done = 1;
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (track == RC_HASH_CDTRACK_FIRST_OF_SECOND_SESSION && session == 2)
|
|
365
|
+
{
|
|
366
|
+
track = current_track.id;
|
|
367
|
+
done = 1;
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
else if (strncasecmp(ptr, "TRACK ", 6) == 0)
|
|
373
|
+
{
|
|
374
|
+
if (current_track.sector_size)
|
|
375
|
+
memcpy(&previous_track, ¤t_track, sizeof(current_track));
|
|
376
|
+
|
|
377
|
+
ptr += 6;
|
|
378
|
+
current_track.id = atoi(ptr);
|
|
379
|
+
|
|
380
|
+
current_track.pregap_sectors = -1;
|
|
381
|
+
current_track.first_sector = -1;
|
|
382
|
+
|
|
383
|
+
while (*ptr != ' ')
|
|
384
|
+
++ptr;
|
|
385
|
+
while (*ptr == ' ')
|
|
386
|
+
++ptr;
|
|
387
|
+
memcpy(current_track.mode, ptr, sizeof(current_track.mode));
|
|
388
|
+
current_track.is_data = (memcmp(current_track.mode, "MODE", 4) == 0);
|
|
389
|
+
|
|
390
|
+
if (current_track.is_data)
|
|
391
|
+
{
|
|
392
|
+
current_track.sector_size = atoi(ptr + 6);
|
|
393
|
+
}
|
|
394
|
+
else
|
|
395
|
+
{
|
|
396
|
+
/* assume AUDIO */
|
|
397
|
+
current_track.sector_size = 2352;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
else if (strncasecmp(ptr, "FILE ", 5) == 0)
|
|
401
|
+
{
|
|
402
|
+
if (current_track.sector_size)
|
|
403
|
+
{
|
|
404
|
+
memcpy(&previous_track, ¤t_track, sizeof(previous_track));
|
|
405
|
+
|
|
406
|
+
if (previous_track.sector_count == 0)
|
|
407
|
+
{
|
|
408
|
+
const int64_t bin_size = cdreader_get_bin_size(path, previous_track.filename, iterator);
|
|
409
|
+
const uint32_t file_sector_count = (uint32_t)bin_size / previous_track.sector_size;
|
|
410
|
+
previous_track.sector_count = file_sector_count - previous_track.first_sector;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/* if looking for the largest data track, check to see if this one is larger */
|
|
414
|
+
if (track == RC_HASH_CDTRACK_LARGEST && previous_track.is_data &&
|
|
415
|
+
previous_track.sector_count > largest_track.sector_count)
|
|
416
|
+
{
|
|
417
|
+
memcpy(&largest_track, &previous_track, sizeof(largest_track));
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
memset(¤t_track, 0, sizeof(current_track));
|
|
422
|
+
|
|
423
|
+
current_track.file_first_sector = previous_track.file_first_sector +
|
|
424
|
+
previous_track.first_sector + previous_track.sector_count;
|
|
425
|
+
|
|
426
|
+
ptr += 5;
|
|
427
|
+
ptr2 = ptr;
|
|
428
|
+
if (*ptr == '"')
|
|
429
|
+
{
|
|
430
|
+
++ptr;
|
|
431
|
+
do
|
|
432
|
+
{
|
|
433
|
+
++ptr2;
|
|
434
|
+
} while (*ptr2 && *ptr2 != '\n' && *ptr2 != '"');
|
|
435
|
+
}
|
|
436
|
+
else
|
|
437
|
+
{
|
|
438
|
+
do
|
|
439
|
+
{
|
|
440
|
+
++ptr2;
|
|
441
|
+
} while (*ptr2 && *ptr2 != '\n' && *ptr2 != ' ');
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (ptr2 - ptr < (int)sizeof(current_track.filename))
|
|
445
|
+
memcpy(current_track.filename, ptr, ptr2 - ptr);
|
|
446
|
+
}
|
|
447
|
+
else if (strncasecmp(ptr, "REM ", 4) == 0)
|
|
448
|
+
{
|
|
449
|
+
ptr += 4;
|
|
450
|
+
while (*ptr == ' ')
|
|
451
|
+
++ptr;
|
|
452
|
+
|
|
453
|
+
if (strncasecmp(ptr, "SESSION ", 8) == 0)
|
|
454
|
+
{
|
|
455
|
+
ptr += 8;
|
|
456
|
+
while (*ptr == ' ')
|
|
457
|
+
++ptr;
|
|
458
|
+
session = atoi(ptr);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
while (*ptr && *ptr != '\n')
|
|
463
|
+
++ptr;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (done)
|
|
467
|
+
break;
|
|
468
|
+
|
|
469
|
+
cue_offset += (ptr - buffer);
|
|
470
|
+
iterator->callbacks.filereader.seek(cue_handle, cue_offset, SEEK_SET);
|
|
471
|
+
|
|
472
|
+
} while (1);
|
|
473
|
+
|
|
474
|
+
if (iterator->callbacks.filereader.close)
|
|
475
|
+
iterator->callbacks.filereader.close(cue_handle);
|
|
476
|
+
|
|
477
|
+
if (track == RC_HASH_CDTRACK_LARGEST)
|
|
478
|
+
{
|
|
479
|
+
if (current_track.sector_size && current_track.is_data)
|
|
480
|
+
{
|
|
481
|
+
const int64_t bin_size = cdreader_get_bin_size(path, current_track.filename, iterator);
|
|
482
|
+
const uint32_t file_sector_count = (uint32_t)bin_size / current_track.sector_size;
|
|
483
|
+
current_track.sector_count = file_sector_count - current_track.first_sector;
|
|
484
|
+
|
|
485
|
+
if (largest_track.sector_count > current_track.sector_count)
|
|
486
|
+
memcpy(¤t_track, &largest_track, sizeof(current_track));
|
|
487
|
+
}
|
|
488
|
+
else
|
|
489
|
+
{
|
|
490
|
+
memcpy(¤t_track, &largest_track, sizeof(current_track));
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
track = current_track.id;
|
|
494
|
+
}
|
|
495
|
+
else if (track == RC_HASH_CDTRACK_LAST && !done)
|
|
496
|
+
{
|
|
497
|
+
track = current_track.id;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (current_track.id == track)
|
|
501
|
+
{
|
|
502
|
+
cdrom = (rc_hash_cdrom_track_t*)calloc(1, sizeof(*cdrom));
|
|
503
|
+
if (!cdrom)
|
|
504
|
+
{
|
|
505
|
+
rc_hash_iterator_error_formatted(iterator, "Failed to allocate %u bytes", (unsigned)sizeof(*cdrom));
|
|
506
|
+
return NULL;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
cdrom->file_reader = &iterator->callbacks.filereader;
|
|
510
|
+
cdrom->file_track_offset = current_track.file_track_offset;
|
|
511
|
+
cdrom->track_pregap_sectors = current_track.pregap_sectors;
|
|
512
|
+
cdrom->track_first_sector = current_track.file_first_sector + current_track.first_sector;
|
|
513
|
+
#ifndef NDEBUG
|
|
514
|
+
cdrom->track_id = current_track.id;
|
|
515
|
+
#endif
|
|
516
|
+
|
|
517
|
+
/* verify existance of bin file */
|
|
518
|
+
bin_filename = cdreader_get_bin_path(path, current_track.filename, iterator);
|
|
519
|
+
if (bin_filename)
|
|
520
|
+
{
|
|
521
|
+
if (cdreader_open_bin(cdrom, bin_filename, current_track.mode))
|
|
522
|
+
{
|
|
523
|
+
if (cdrom->track_pregap_sectors)
|
|
524
|
+
rc_hash_iterator_verbose_formatted(iterator, "Opened track %d (sector size %d, %d pregap sectors)",
|
|
525
|
+
track, cdrom->sector_size, cdrom->track_pregap_sectors);
|
|
526
|
+
else
|
|
527
|
+
rc_hash_iterator_verbose_formatted(iterator, "Opened track %d (sector size %d)", track, cdrom->sector_size);
|
|
528
|
+
}
|
|
529
|
+
else
|
|
530
|
+
{
|
|
531
|
+
if (cdrom->file_handle)
|
|
532
|
+
{
|
|
533
|
+
cdrom->file_reader->close(cdrom->file_handle);
|
|
534
|
+
rc_hash_iterator_error_formatted(iterator, "Could not determine sector size for %s track", current_track.mode);
|
|
535
|
+
}
|
|
536
|
+
else
|
|
537
|
+
{
|
|
538
|
+
rc_hash_iterator_error_formatted(iterator, "Could not open %s", bin_filename);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
free(cdrom);
|
|
542
|
+
cdrom = NULL;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
free(bin_filename);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return cdrom;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
static void* cdreader_open_gdi_track(const char* path, uint32_t track, const rc_hash_iterator_t* iterator)
|
|
553
|
+
{
|
|
554
|
+
void* file_handle;
|
|
555
|
+
char buffer[1024];
|
|
556
|
+
char mode[16] = "MODE1/";
|
|
557
|
+
char sector_size[16];
|
|
558
|
+
char file[256];
|
|
559
|
+
int64_t track_size;
|
|
560
|
+
int track_type;
|
|
561
|
+
char* bin_path = NULL;
|
|
562
|
+
uint32_t current_track = 0;
|
|
563
|
+
char* ptr, *ptr2, *end;
|
|
564
|
+
int lba = 0;
|
|
565
|
+
|
|
566
|
+
uint32_t largest_track = 0;
|
|
567
|
+
int64_t largest_track_size = 0;
|
|
568
|
+
char largest_track_file[256];
|
|
569
|
+
char largest_track_sector_size[16];
|
|
570
|
+
int largest_track_lba = 0;
|
|
571
|
+
|
|
572
|
+
int found = 0;
|
|
573
|
+
size_t num_read = 0;
|
|
574
|
+
int64_t file_offset = 0;
|
|
575
|
+
rc_hash_cdrom_track_t* cdrom = NULL;
|
|
576
|
+
|
|
577
|
+
file_handle = iterator->callbacks.filereader.open(path);
|
|
578
|
+
if (!file_handle)
|
|
579
|
+
return NULL;
|
|
580
|
+
|
|
581
|
+
file[0] = '\0';
|
|
582
|
+
do
|
|
583
|
+
{
|
|
584
|
+
num_read = iterator->callbacks.filereader.read(file_handle, buffer, sizeof(buffer) - 1);
|
|
585
|
+
if (num_read == 0)
|
|
586
|
+
break;
|
|
587
|
+
|
|
588
|
+
buffer[num_read] = 0;
|
|
589
|
+
if (num_read == sizeof(buffer) - 1)
|
|
590
|
+
end = buffer + sizeof(buffer) * 3 / 4;
|
|
591
|
+
else
|
|
592
|
+
end = buffer + num_read;
|
|
593
|
+
|
|
594
|
+
ptr = buffer;
|
|
595
|
+
|
|
596
|
+
/* the first line contains the number of tracks, so we can get the last track index from it */
|
|
597
|
+
if (track == RC_HASH_CDTRACK_LAST)
|
|
598
|
+
track = atoi(ptr);
|
|
599
|
+
|
|
600
|
+
/* first line contains the number of tracks and will be skipped */
|
|
601
|
+
while (ptr < end)
|
|
602
|
+
{
|
|
603
|
+
/* skip until next newline */
|
|
604
|
+
while (*ptr != '\n' && ptr < end)
|
|
605
|
+
++ptr;
|
|
606
|
+
|
|
607
|
+
/* skip newlines */
|
|
608
|
+
while ((*ptr == '\n' || *ptr == '\r') && ptr < end)
|
|
609
|
+
++ptr;
|
|
610
|
+
|
|
611
|
+
/* line format: [trackid] [lba] [type] [sectorsize] [file] [?] */
|
|
612
|
+
while (isspace((unsigned char)*ptr))
|
|
613
|
+
++ptr;
|
|
614
|
+
|
|
615
|
+
current_track = (uint32_t)atoi(ptr);
|
|
616
|
+
if (track && current_track != track && track != RC_HASH_CDTRACK_FIRST_DATA)
|
|
617
|
+
continue;
|
|
618
|
+
|
|
619
|
+
while (isdigit((unsigned char)*ptr))
|
|
620
|
+
++ptr;
|
|
621
|
+
++ptr;
|
|
622
|
+
|
|
623
|
+
while (isspace((unsigned char)*ptr))
|
|
624
|
+
++ptr;
|
|
625
|
+
|
|
626
|
+
lba = atoi(ptr);
|
|
627
|
+
while (isdigit((unsigned char)*ptr))
|
|
628
|
+
++ptr;
|
|
629
|
+
++ptr;
|
|
630
|
+
|
|
631
|
+
while (isspace((unsigned char)*ptr))
|
|
632
|
+
++ptr;
|
|
633
|
+
|
|
634
|
+
track_type = atoi(ptr);
|
|
635
|
+
while (isdigit((unsigned char)*ptr))
|
|
636
|
+
++ptr;
|
|
637
|
+
++ptr;
|
|
638
|
+
|
|
639
|
+
while (isspace((unsigned char)*ptr))
|
|
640
|
+
++ptr;
|
|
641
|
+
|
|
642
|
+
ptr2 = sector_size;
|
|
643
|
+
while (isdigit((unsigned char)*ptr))
|
|
644
|
+
*ptr2++ = *ptr++;
|
|
645
|
+
*ptr2 = '\0';
|
|
646
|
+
++ptr;
|
|
647
|
+
|
|
648
|
+
while (isspace((unsigned char)*ptr))
|
|
649
|
+
++ptr;
|
|
650
|
+
|
|
651
|
+
ptr2 = file;
|
|
652
|
+
if (*ptr == '\"')
|
|
653
|
+
{
|
|
654
|
+
++ptr;
|
|
655
|
+
while (*ptr != '\"')
|
|
656
|
+
*ptr2++ = *ptr++;
|
|
657
|
+
++ptr;
|
|
658
|
+
}
|
|
659
|
+
else
|
|
660
|
+
{
|
|
661
|
+
while (*ptr != ' ')
|
|
662
|
+
*ptr2++ = *ptr++;
|
|
663
|
+
}
|
|
664
|
+
*ptr2 = '\0';
|
|
665
|
+
|
|
666
|
+
if (track == current_track)
|
|
667
|
+
{
|
|
668
|
+
found = 1;
|
|
669
|
+
break;
|
|
670
|
+
}
|
|
671
|
+
else if (track == RC_HASH_CDTRACK_FIRST_DATA && track_type == 4)
|
|
672
|
+
{
|
|
673
|
+
track = current_track;
|
|
674
|
+
found = 1;
|
|
675
|
+
break;
|
|
676
|
+
}
|
|
677
|
+
else if (track == RC_HASH_CDTRACK_LARGEST && track_type == 4)
|
|
678
|
+
{
|
|
679
|
+
track_size = cdreader_get_bin_size(path, file, iterator);
|
|
680
|
+
if (track_size > largest_track_size)
|
|
681
|
+
{
|
|
682
|
+
largest_track_size = track_size;
|
|
683
|
+
largest_track = current_track;
|
|
684
|
+
largest_track_lba = lba;
|
|
685
|
+
strcpy_s(largest_track_file, sizeof(largest_track_file), file);
|
|
686
|
+
strcpy_s(largest_track_sector_size, sizeof(largest_track_sector_size), sector_size);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (found)
|
|
692
|
+
break;
|
|
693
|
+
|
|
694
|
+
file_offset += (ptr - buffer);
|
|
695
|
+
iterator->callbacks.filereader.seek(file_handle, file_offset, SEEK_SET);
|
|
696
|
+
|
|
697
|
+
} while (1);
|
|
698
|
+
|
|
699
|
+
if (iterator->callbacks.filereader.close)
|
|
700
|
+
iterator->callbacks.filereader.close(file_handle);
|
|
701
|
+
|
|
702
|
+
cdrom = (rc_hash_cdrom_track_t*)calloc(1, sizeof(*cdrom));
|
|
703
|
+
if (!cdrom)
|
|
704
|
+
{
|
|
705
|
+
rc_hash_iterator_error_formatted(iterator, "Failed to allocate %u bytes", (unsigned)sizeof(*cdrom));
|
|
706
|
+
return NULL;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
cdrom->file_reader = &iterator->callbacks.filereader;
|
|
710
|
+
|
|
711
|
+
/* if we were tracking the largest track, make it the current track.
|
|
712
|
+
* otherwise, current_track will be the requested track, or last track. */
|
|
713
|
+
if (largest_track != 0 && largest_track != current_track)
|
|
714
|
+
{
|
|
715
|
+
current_track = largest_track;
|
|
716
|
+
strcpy_s(file, sizeof(file), largest_track_file);
|
|
717
|
+
strcpy_s(sector_size, sizeof(sector_size), largest_track_sector_size);
|
|
718
|
+
lba = largest_track_lba;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/* open the bin file for the track - construct mode parameter from sector_size */
|
|
722
|
+
ptr = &mode[6];
|
|
723
|
+
ptr2 = sector_size;
|
|
724
|
+
while (*ptr2 && *ptr2 != '\"')
|
|
725
|
+
*ptr++ = *ptr2++;
|
|
726
|
+
*ptr = '\0';
|
|
727
|
+
|
|
728
|
+
bin_path = cdreader_get_bin_path(path, file, iterator);
|
|
729
|
+
if (cdreader_open_bin(cdrom, bin_path, mode))
|
|
730
|
+
{
|
|
731
|
+
cdrom->track_pregap_sectors = 0;
|
|
732
|
+
cdrom->track_first_sector = lba;
|
|
733
|
+
#ifndef NDEBUG
|
|
734
|
+
cdrom->track_id = current_track;
|
|
735
|
+
#endif
|
|
736
|
+
|
|
737
|
+
rc_hash_iterator_verbose_formatted(iterator, "Opened track %d (sector size %d)", current_track, cdrom->sector_size);
|
|
738
|
+
}
|
|
739
|
+
else
|
|
740
|
+
{
|
|
741
|
+
rc_hash_iterator_error_formatted(iterator, "Could not open %s", bin_path);
|
|
742
|
+
|
|
743
|
+
free(cdrom);
|
|
744
|
+
cdrom = NULL;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
free(bin_path);
|
|
748
|
+
|
|
749
|
+
return cdrom;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
static void* cdreader_open_track_iterator(const char* path, uint32_t track, const rc_hash_iterator_t* iterator)
|
|
753
|
+
{
|
|
754
|
+
/* backwards compatibility - 0 used to mean largest */
|
|
755
|
+
if (track == 0)
|
|
756
|
+
track = RC_HASH_CDTRACK_LARGEST;
|
|
757
|
+
|
|
758
|
+
if (rc_path_compare_extension(path, "cue"))
|
|
759
|
+
return cdreader_open_cue_track(path, track, iterator);
|
|
760
|
+
if (rc_path_compare_extension(path, "gdi"))
|
|
761
|
+
return cdreader_open_gdi_track(path, track, iterator);
|
|
762
|
+
|
|
763
|
+
return cdreader_open_bin_track(path, track, iterator);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
static size_t cdreader_read_sector(void* track_handle, uint32_t sector, void* buffer, size_t requested_bytes)
|
|
767
|
+
{
|
|
768
|
+
int64_t sector_start;
|
|
769
|
+
size_t num_read, total_read = 0;
|
|
770
|
+
uint8_t* buffer_ptr = (uint8_t*)buffer;
|
|
771
|
+
|
|
772
|
+
rc_hash_cdrom_track_t* cdrom = (rc_hash_cdrom_track_t*)track_handle;
|
|
773
|
+
if (!cdrom)
|
|
774
|
+
return 0;
|
|
775
|
+
|
|
776
|
+
if (sector < (uint32_t)cdrom->track_first_sector)
|
|
777
|
+
return 0;
|
|
778
|
+
|
|
779
|
+
sector_start = (int64_t)(sector - cdrom->track_first_sector) * cdrom->sector_size +
|
|
780
|
+
cdrom->sector_header_size + cdrom->file_track_offset;
|
|
781
|
+
|
|
782
|
+
while (requested_bytes > (size_t)cdrom->raw_data_size)
|
|
783
|
+
{
|
|
784
|
+
cdrom->file_reader->seek(cdrom->file_handle, sector_start, SEEK_SET);
|
|
785
|
+
num_read = cdrom->file_reader->read(cdrom->file_handle, buffer_ptr, cdrom->raw_data_size);
|
|
786
|
+
total_read += num_read;
|
|
787
|
+
|
|
788
|
+
if (num_read < (size_t)cdrom->raw_data_size)
|
|
789
|
+
return total_read;
|
|
790
|
+
|
|
791
|
+
buffer_ptr += cdrom->raw_data_size;
|
|
792
|
+
sector_start += cdrom->sector_size;
|
|
793
|
+
requested_bytes -= cdrom->raw_data_size;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
cdrom->file_reader->seek(cdrom->file_handle, sector_start, SEEK_SET);
|
|
797
|
+
num_read = cdrom->file_reader->read(cdrom->file_handle, buffer_ptr, (int)requested_bytes);
|
|
798
|
+
total_read += num_read;
|
|
799
|
+
|
|
800
|
+
return total_read;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
static void cdreader_close_track(void* track_handle)
|
|
804
|
+
{
|
|
805
|
+
rc_hash_cdrom_track_t* cdrom = (rc_hash_cdrom_track_t*)track_handle;
|
|
806
|
+
if (cdrom)
|
|
807
|
+
{
|
|
808
|
+
if (cdrom->file_handle && cdrom->file_reader->close)
|
|
809
|
+
cdrom->file_reader->close(cdrom->file_handle);
|
|
810
|
+
|
|
811
|
+
free(track_handle);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
static uint32_t cdreader_first_track_sector(void* track_handle)
|
|
816
|
+
{
|
|
817
|
+
rc_hash_cdrom_track_t* cdrom = (rc_hash_cdrom_track_t*)track_handle;
|
|
818
|
+
if (cdrom)
|
|
819
|
+
return cdrom->track_first_sector + cdrom->track_pregap_sectors;
|
|
820
|
+
|
|
821
|
+
return 0;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
void rc_hash_get_default_cdreader(struct rc_hash_cdreader* cdreader)
|
|
825
|
+
{
|
|
826
|
+
cdreader->open_track = NULL;
|
|
827
|
+
cdreader->read_sector = cdreader_read_sector;
|
|
828
|
+
cdreader->close_track = cdreader_close_track;
|
|
829
|
+
cdreader->first_track_sector = cdreader_first_track_sector;
|
|
830
|
+
cdreader->open_track_iterator = cdreader_open_track_iterator;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
void rc_hash_init_default_cdreader(void)
|
|
834
|
+
{
|
|
835
|
+
struct rc_hash_cdreader cdreader;
|
|
836
|
+
rc_hash_get_default_cdreader(&cdreader);
|
|
837
|
+
rc_hash_init_custom_cdreader(&cdreader);
|
|
838
|
+
}
|