#include "rc_hash.h" #include "../rc_compat.h" #include "../test_framework.h" #include "data.h" #include "mock_filereader.h" #include static int hash_mock_file(const char* filename, char hash[33], uint32_t console_id, const uint8_t* buffer, size_t buffer_size) { mock_file(0, filename, buffer, buffer_size); return rc_hash_generate_from_file(hash, console_id, filename); } static void iterate_mock_file(struct rc_hash_iterator *iterator, const char* filename, const uint8_t* buffer, size_t buffer_size) { mock_file(0, filename, buffer, buffer_size); rc_hash_initialize_iterator(iterator, filename, NULL, 0); } /* ========================================================================= */ static void test_hash_full_file(uint32_t console_id, const char* filename, size_t size, const char* expected_md5) { uint8_t* image = generate_generic_file(size); char hash_buffer[33], hash_file[33], hash_iterator[33]; /* test full buffer hash */ int result_buffer = rc_hash_generate_from_buffer(hash_buffer, console_id, image, size); /* test full file hash */ int result_file = hash_mock_file(filename, hash_file, console_id, image, size); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; iterate_mock_file(&iterator, filename, image, size); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_buffer, 1); ASSERT_STR_EQUALS(hash_buffer, expected_md5); ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_unknown_format(uint32_t console_id, const char* path) { char hash_file[33] = "", hash_iterator[33] = ""; /* test file hash (won't match) */ int result_file = rc_hash_generate_from_file(hash_file, console_id, path); /* test file identification from iterator (won't match) */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, path, NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* validation */ ASSERT_NUM_EQUALS(result_file, 0); ASSERT_STR_EQUALS(hash_file, ""); ASSERT_NUM_EQUALS(result_iterator, 0); ASSERT_STR_EQUALS(hash_iterator, ""); } static void test_hash_m3u(uint32_t console_id, const char* filename, size_t size, const char* expected_md5) { uint8_t* image = generate_generic_file(size); char hash_file[33], hash_iterator[33]; const char* m3u_filename = "test.m3u"; mock_file(0, filename, image, size); mock_file(1, m3u_filename, (uint8_t*)filename, strlen(filename)); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, console_id, m3u_filename); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, m3u_filename, NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_filename(uint32_t console_id, const char* path, const char* expected_md5) { char hash_file[33], hash_iterator[33]; /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, console_id, path); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, path, NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } /* ========================================================================= */ static void test_hash_3do_bin() { size_t image_size; uint8_t* image = generate_3do_bin(1, 123456, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "9b2266b8f5abed9c12cce780750e88d6"; mock_file(0, "game.bin", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_3DO, "game.bin"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; mock_file_size(0, 45678901); /* must be > 32MB for iterator to consider CD formats for bin */ rc_hash_initialize_iterator(&iterator, "game.bin", NULL, 0); mock_file_size(0, image_size); /* change it back before doing the hashing */ result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_3do_cue() { size_t image_size; uint8_t* image = generate_3do_bin(1, 9347, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "257d1d19365a864266b236214dbea29c"; mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_3DO, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_3do_iso() { size_t image_size; uint8_t* image = generate_3do_bin(1, 9347, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "257d1d19365a864266b236214dbea29c"; mock_file(0, "game.iso", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_3DO, "game.iso"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.iso", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_3do_invalid_header() { /* this is meant to simulate attempting to open a non-3DO CD. TODO: generate PSX CD */ size_t image_size; uint8_t* image = generate_3do_bin(1, 12, &image_size); char hash_file[33]; /* make the header not match */ image[3] = 0x34; mock_file(0, "game.bin", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_3DO, "game.bin"); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 0); } static void test_hash_3do_launchme_case_insensitive() { /* main executable for "Captain Quazar" is "launchme" */ /* main executable for "Rise of the Robots" is "launchMe" */ /* main executable for "Road Rash" is "LaunchMe" */ /* main executable for "Sewer Shark" is "Launchme" */ size_t image_size; uint8_t* image = generate_3do_bin(1, 6543, &image_size); char hash_file[33]; const char* expected_md5 = "59622882e3261237e8a1e396825ae4f5"; memcpy(&image[2048 + 0x14 + 0x48 + 0x20], "launchme", 8); mock_file(0, "game.bin", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_3DO, "game.bin"); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); } static void test_hash_3do_no_launchme() { /* this case should not happen */ size_t image_size; uint8_t* image = generate_3do_bin(1, 6543, &image_size); char hash_file[33]; memcpy(&image[2048 + 0x14 + 0x48 + 0x20], "filename", 8); mock_file(0, "game.bin", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_3DO, "game.bin"); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 0); } static void test_hash_3do_long_directory() { /* root directory for "Dragon's Lair" uses more than one sector */ size_t image_size; uint8_t* image = generate_3do_bin(3, 6543, &image_size); char hash_file[33]; const char* expected_md5 = "8979e876ae502e0f79218f7ff7bd8c2a"; mock_file(0, "game.bin", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_3DO, "game.bin"); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); } /* ========================================================================= */ static void test_hash_arduboy() { char hash_file[33], hash_iterator[33]; const char* expected_md5 = "67b64633285a7f965064ba29dab45148"; const char* hex_input = ":100000000C94690D0C94910D0C94910D0C94910D20\n" ":100010000C94910D0C94910D0C94910D0C94910DE8\n" ":100020000C94910D0C94910D0C94C32A0C94352BC7\n" ":00000001FF\n"; mock_file(0, "game.hex", (const uint8_t*)hex_input, strlen(hex_input)); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ARDUBOY, "game.hex"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.hex", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_arduboy_crlf() { char hash_file[33], hash_iterator[33]; const char* expected_md5 = "67b64633285a7f965064ba29dab45148"; const char* hex_input = ":100000000C94690D0C94910D0C94910D0C94910D20\r\n" ":100010000C94910D0C94910D0C94910D0C94910DE8\r\n" ":100020000C94910D0C94910D0C94C32A0C94352BC7\r\n" ":00000001FF\r\n"; mock_file(0, "game.hex", (const uint8_t*)hex_input, strlen(hex_input)); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ARDUBOY, "game.hex"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.hex", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_arduboy_no_final_lf() { char hash_file[33], hash_iterator[33]; const char* expected_md5 = "67b64633285a7f965064ba29dab45148"; const char* hex_input = ":100000000C94690D0C94910D0C94910D0C94910D20\n" ":100010000C94910D0C94910D0C94910D0C94910DE8\n" ":100020000C94910D0C94910D0C94C32A0C94352BC7\n" ":00000001FF"; mock_file(0, "game.hex", (const uint8_t*)hex_input, strlen(hex_input)); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ARDUBOY, "game.hex"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.hex", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } /* ========================================================================= */ static void test_hash_atari_7800() { size_t image_size; uint8_t* image = generate_atari_7800_file(16, 0, &image_size); char hash[33]; int result = rc_hash_generate_from_buffer(hash, RC_CONSOLE_ATARI_7800, image, image_size); free(image); ASSERT_NUM_EQUALS(result, 1); ASSERT_STR_EQUALS(hash, "455f07d8500f3fabc54906737866167f"); ASSERT_NUM_EQUALS(image_size, 16384); } static void test_hash_atari_7800_with_header() { size_t image_size; uint8_t* image = generate_atari_7800_file(16, 1, &image_size); char hash[33]; int result = rc_hash_generate_from_buffer(hash, RC_CONSOLE_ATARI_7800, image, image_size); free(image); /* NOTE: expectation is that this hash matches the hash in test_hash_atari_7800 */ ASSERT_NUM_EQUALS(result, 1); ASSERT_STR_EQUALS(hash, "455f07d8500f3fabc54906737866167f"); ASSERT_NUM_EQUALS(image_size, 16384 + 128); } /* ========================================================================= */ static void test_hash_atari_jaguar_cd() { const char* cue_file = "REM SESSION 01\n" "FILE \"track01.bin\" BINARY\n" " TRACK 01 AUDIO\n" " INDEX 01 00:00:00\n" "REM SESSION 02\n" "FILE \"track02.bin\" BINARY\n" " TRACK 02 AUDIO\n" " INDEX 01 00:00:00\n" "FILE \"track03.bin\" BINARY\n" " TRACK 03 AUDIO\n" " INDEX 01 00:00:00\n"; size_t image_size; uint8_t* image = generate_jaguarcd_bin(2, 60024, 0, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "c324d95dc5831c2d5c470eefb18c346b"; mock_file(0, "game.cue", (uint8_t*)cue_file, strlen(cue_file)); mock_file(1, "track02.bin", image, image_size); rc_hash_init_default_cdreader(); /* want to test actual FIRST_OF_SECOND_SESSION calculation */ /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ATARI_JAGUAR_CD, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); init_mock_cdreader(); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_atari_jaguar_cd_byteswapped() { const char* cue_file = "REM SESSION 01\n" "FILE \"track01.bin\" BINARY\n" " TRACK 01 AUDIO\n" " INDEX 01 00:00:00\n" "REM SESSION 02\n" "FILE \"track02.bin\" BINARY\n" " TRACK 02 AUDIO\n" " INDEX 01 00:00:00\n" "FILE \"track03.bin\" BINARY\n" " TRACK 03 AUDIO\n" " INDEX 01 00:00:00\n"; size_t image_size; uint8_t* image = generate_jaguarcd_bin(2, 60024, 1, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "c324d95dc5831c2d5c470eefb18c346b"; mock_file(0, "game.cue", (uint8_t*)cue_file, strlen(cue_file)); mock_file(1, "track02.bin", image, image_size); rc_hash_init_default_cdreader(); /* want to test actual FIRST_OF_SECOND_SESSION calculation */ /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ATARI_JAGUAR_CD, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); init_mock_cdreader(); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_atari_jaguar_cd_track3() { const char* cue_file = "REM SESSION 01\n" "FILE \"track01.bin\" BINARY\n" " TRACK 01 AUDIO\n" " INDEX 01 00:00:00\n" "FILE \"track02.bin\" BINARY\n" " TRACK 02 AUDIO\n" " INDEX 01 00:00:00\n" "REM SESSION 02\n" "FILE \"track03.bin\" BINARY\n" " TRACK 03 AUDIO\n" " INDEX 01 00:00:00\n"; size_t image_size; uint8_t* image = generate_jaguarcd_bin(1470, 99200, 1, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "060e9d223c584b581cf7d7ce17c0e5dc"; mock_file(0, "game.cue", (uint8_t*)cue_file, strlen(cue_file)); mock_file(1, "track03.bin", image, image_size); rc_hash_init_default_cdreader(); /* want to test actual FIRST_OF_SECOND_SESSION calculation */ /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ATARI_JAGUAR_CD, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); init_mock_cdreader(); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_atari_jaguar_cd_no_header() { const char* cue_file = "REM SESSION 01\n" "FILE \"track01.bin\" BINARY\n" " TRACK 01 AUDIO\n" " INDEX 01 00:00:00\n" "REM SESSION 02\n" "FILE \"track02.bin\" BINARY\n" " TRACK 02 AUDIO\n" " INDEX 01 00:00:00\n" "FILE \"track03.bin\" BINARY\n" " TRACK 03 AUDIO\n" " INDEX 01 00:00:00\n"; size_t image_size; uint8_t* image = generate_jaguarcd_bin(2, 32768, 1, &image_size); char hash_file[33], hash_iterator[33]; image[2 + 64 + 12] = 'B'; /* corrupt the header */ mock_file(0, "game.cue", (uint8_t*)cue_file, strlen(cue_file)); mock_file(1, "track02.bin", image, image_size); rc_hash_init_default_cdreader(); /* want to test actual FIRST_OF_SECOND_SESSION calculation */ /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ATARI_JAGUAR_CD, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); init_mock_cdreader(); /* validation */ ASSERT_NUM_EQUALS(result_file, 0); ASSERT_NUM_EQUALS(result_iterator, 0); } static void test_hash_atari_jaguar_cd_no_sessions() { const char* cue_file = "FILE \"track01.bin\" BINARY\n" " TRACK 01 AUDIO\n" " INDEX 01 00:00:00\n" "FILE \"track02.bin\" BINARY\n" " TRACK 02 AUDIO\n" " INDEX 01 00:00:00\n" "FILE \"track03.bin\" BINARY\n" " TRACK 03 AUDIO\n" " INDEX 01 00:00:00\n"; size_t image_size; uint8_t* image = generate_jaguarcd_bin(2, 99200, 1, &image_size); char hash_file[33], hash_iterator[33]; mock_file(0, "game.cue", (uint8_t*)cue_file, strlen(cue_file)); mock_file(1, "track03.bin", image, image_size); rc_hash_init_default_cdreader(); /* want to test actual FIRST_OF_SECOND_SESSION calculation */ /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ATARI_JAGUAR_CD, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); init_mock_cdreader(); /* validation */ ASSERT_NUM_EQUALS(result_file, 0); ASSERT_NUM_EQUALS(result_iterator, 0); } extern const char* _rc_hash_jaguar_cd_homebrew_hash; static void test_hash_atari_jaguar_cd_homebrew() { /* Jaguar CD homebrew games all appear to have a common bootloader in the primary boot executable space. They only * differ in a secondary executable in the second track (part of the first session). This doesn't appear to be * intentional behavior based on the CD BIOS documentation, which states that all developer code should be in the * first track of the second session. I speculate this is done to work around the authentication logic. */ const char* cue_file = "REM SESSION 01\n" "FILE \"track01.bin\" BINARY\n" " TRACK 01 AUDIO\n" " INDEX 01 00:00:00\n" "FILE \"track02.bin\" BINARY\n" " TRACK 02 AUDIO\n" " INDEX 01 00:00:00\n" "REM SESSION 02\n" "FILE \"track03.bin\" BINARY\n" " TRACK 03 AUDIO\n" " INDEX 01 00:00:00\n"; size_t image_size, image_size2; uint8_t* image = generate_jaguarcd_bin(2, 45760, 1, &image_size); uint8_t* image2 = generate_jaguarcd_bin(2, 986742, 1, &image_size2); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "3fdf70e362c845524c9e447aacaed0a9"; image2[0x60] = 0x21; /* ATARI APPROVED DATA HEADER ATRI! */ memcpy(&image2[0xA2], &image2[0x62], 8); /* addr / size */ memcpy(&image2[0x62], "RTKARTKARTKARTKA", 16); /* KARTKARTKARTKART */ memcpy(&image2[0x72], "RTKARTKARTKARTKA", 16); memcpy(&image2[0x82], "RTKARTKARTKARTKA", 16); memcpy(&image2[0x92], "RTKARTKARTKARTKA", 16); mock_file(0, "game.cue", (uint8_t*)cue_file, strlen(cue_file)); mock_file(2, "track02.bin", image2, image_size2); mock_file(1, "track03.bin", image, image_size); rc_hash_init_default_cdreader(); /* want to test actual FIRST_OF_SECOND_SESSION calculation */ _rc_hash_jaguar_cd_homebrew_hash = "4e4114b2675eff21bb77dd41e141ddd6"; /* mock the hash of the homebrew bootloader */ /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_ATARI_JAGUAR_CD, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ _rc_hash_jaguar_cd_homebrew_hash = NULL; free(image); free(image2); init_mock_cdreader(); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } /* ========================================================================= */ static void test_hash_dreamcast_single_bin() { size_t image_size; uint8_t* image = generate_dreamcast_bin(45000, 1458208, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "2a550500caee9f06e5d061fe10a46f6e"; mock_file(0, "track03.bin", image, image_size); mock_file_first_sector(0, 45000); mock_file(1, "game.gdi", (uint8_t*)"game.bin", 8); mock_cd_num_tracks(3); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_DREAMCAST, "game.gdi"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.gdi", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_dreamcast_split_bin() { size_t image_size; uint8_t* image = generate_dreamcast_bin(548106, 1830912, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "771e56aff169230ede4505013a4bcf9f"; mock_file(0, "game.gdi", (uint8_t*)"game.bin", 8); mock_file(1, "track03.bin", image, image_size); mock_file_first_sector(1, 45000); mock_file(2, "track26.bin", image, image_size); mock_file_first_sector(2, 548106); mock_cd_num_tracks(26); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_DREAMCAST, "game.gdi"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.gdi", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_dreamcast_cue() { const char* cue_file = "FILE \"track01.bin\" BINARY\n" " TRACK 01 MODE1/2352\n" " INDEX 01 00:00:00\n" "FILE \"track02.bin\" BINARY\n" " TRACK 02 AUDIO\n" " INDEX 00 00:00:00\n" " INDEX 01 00:02:00\n" "FILE \"track03.bin\" BINARY\n" " TRACK 03 MODE1/2352\n" " INDEX 01 00:00:00\n" "FILE \"track04.bin\" BINARY\n" " TRACK 04 AUDIO\n" " INDEX 00 00:00:00\n" " INDEX 01 00:02:00\n" "FILE \"track05.bin\" BINARY\n" " TRACK 05 MODE1/2352\n" " INDEX 00 00:00:00\n" " INDEX 01 00:03:00\n"; size_t image_size; uint8_t* image = convert_to_2352(generate_dreamcast_bin(45000, 1697028, &image_size), &image_size, 45000); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "c952864c3364591d2a8793ce2cfbf3a0"; mock_file(0, "game.cue", (uint8_t*)cue_file, strlen(cue_file)); mock_file(1, "track01.bin", image, 1425312); /* 606 sectors */ mock_file(2, "track02.bin", image, 1589952); /* 676 sectors */ mock_file(3, "track03.bin", image, image_size); /* 737 sectors */ mock_file(4, "track04.bin", image, 1237152); /* 526 sectors */ mock_file(5, "track05.bin", image, image_size); rc_hash_init_default_cdreader(); /* want to test actual first_track_sector calculation */ /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_DREAMCAST, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); init_mock_cdreader(); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } /* ========================================================================= */ static void test_hash_gamecube() { size_t image_size; uint8_t* image = generate_gamecube_iso(32, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "c7803b704fa43d22d8f6e55f4789cb45"; mock_file(0, "test.iso", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_GAMECUBE, "test.iso"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "test.iso", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); ASSERT_NUM_EQUALS(image_size, 32 * 1024 * 1024); } /* ========================================================================= */ static void test_hash_msdos_dosz() { size_t image_size; uint8_t* image = generate_zip_file(&image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "59a255662262f5ada32791b8c36e8ea7"; mock_file(0, "game.dosz", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_MS_DOS, "game.dosz"); /* test file identification from iterator */ struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.dosz", NULL, 0); int result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_msdos_dosz_zip64() { size_t image_size; uint8_t* image = generate_zip64_file(&image_size); char hash_file[33]; const char* expected_md5 = "927dad0a57a2860267ab7bcdb8bc3f61"; mock_file(0, "game.dosz", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_MS_DOS, "game.dosz"); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); } static void test_hash_msdos_dosz_with_dosc() { size_t image_size; uint8_t* image = generate_zip_file(&image_size); char hash_dosc[33]; const char* expected_dosc_md5 = "dd0c0b0c170c30722784e5e962764c35"; /* Add main dosz file and overlay dosc file which will get hashed together */ mock_file(0, "game.dosz", image, image_size); mock_file(1, "game.dosc", image, image_size); /* test file hash */ int result_dosc = rc_hash_generate_from_file(hash_dosc, RC_CONSOLE_MS_DOS, "game.dosz"); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_dosc, 1); ASSERT_STR_EQUALS(hash_dosc, expected_dosc_md5); } static void test_hash_msdos_dosz_with_parent() { size_t image_base_size, image_child_size; uint8_t* image_base = generate_zip_file(&image_base_size); uint8_t* image_child = generate_child_dosz_file(&image_child_size); char hash_dosz[33], hash_dosc[33], hash_dosc2[33]; const char* expected_dosz_md5 = "623c759476b8b5adb46362f8f0b60769"; const char* expected_dosc_md5 = "ecd9d776cbaad63094829d7b8dbe5959"; const char* expected_dosc2_md5 = "cb55c123936ad84479032ea6444cb1a1"; /* Add base dosz file and child dosz file which will get hashed together */ mock_file(0, "base.dosz", image_base, image_base_size); mock_file(1, "child.dosz", image_child, image_child_size); /* test file hash */ int result_dosz = rc_hash_generate_from_file(hash_dosz, RC_CONSOLE_MS_DOS, "child.dosz"); /* test file hash with base.dosc also existing */ mock_file(2, "base.dosc", image_base, image_base_size); int result_dosc = rc_hash_generate_from_file(hash_dosc, RC_CONSOLE_MS_DOS, "child.dosz"); /* test file hash with child.dosc also existing */ mock_file(3, "child.dosc", image_base, image_base_size); int result_dosc2 = rc_hash_generate_from_file(hash_dosc2, RC_CONSOLE_MS_DOS, "child.dosz"); /* cleanup */ free(image_base); free(image_child); /* validation */ ASSERT_NUM_EQUALS(result_dosz, 1); ASSERT_NUM_EQUALS(result_dosc, 1); ASSERT_NUM_EQUALS(result_dosc2, 1); ASSERT_STR_EQUALS(hash_dosz, expected_dosz_md5); ASSERT_STR_EQUALS(hash_dosc, expected_dosc_md5); ASSERT_STR_EQUALS(hash_dosc2, expected_dosc2_md5); } /* ========================================================================= */ static void test_hash_nes_32k() { size_t image_size; uint8_t* image = generate_nes_file(32, 0, &image_size); char hash[33]; int result = rc_hash_generate_from_buffer(hash, RC_CONSOLE_NINTENDO, image, image_size); free(image); ASSERT_NUM_EQUALS(result, 1); ASSERT_STR_EQUALS(hash, "6a2305a2b6675a97ff792709be1ca857"); ASSERT_NUM_EQUALS(image_size, 32768); } static void test_hash_nes_32k_with_header() { size_t image_size; uint8_t* image = generate_nes_file(32, 1, &image_size); char hash[33]; int result = rc_hash_generate_from_buffer(hash, RC_CONSOLE_NINTENDO, image, image_size); free(image); /* NOTE: expectation is that this hash matches the hash in test_hash_nes_32k */ ASSERT_NUM_EQUALS(result, 1); ASSERT_STR_EQUALS(hash, "6a2305a2b6675a97ff792709be1ca857"); ASSERT_NUM_EQUALS(image_size, 32768 + 16); } static void test_hash_nes_256k() { size_t image_size; uint8_t* image = generate_nes_file(256, 0, &image_size); char hash[33]; int result = rc_hash_generate_from_buffer(hash, RC_CONSOLE_NINTENDO, image, image_size); free(image); ASSERT_NUM_EQUALS(result, 1); ASSERT_STR_EQUALS(hash, "545d527301b8ae148153988d6c4fcb84"); ASSERT_NUM_EQUALS(image_size, 262144); } static void test_hash_fds_two_sides() { size_t image_size; uint8_t* image = generate_fds_file(2, 0, &image_size); char hash[33]; int result = rc_hash_generate_from_buffer(hash, RC_CONSOLE_NINTENDO, image, image_size); free(image); ASSERT_NUM_EQUALS(result, 1); ASSERT_STR_EQUALS(hash, "fd770d4d34c00760fabda6ad294a8f0b"); ASSERT_NUM_EQUALS(image_size, 65500 * 2); } static void test_hash_fds_two_sides_with_header() { size_t image_size; uint8_t* image = generate_fds_file(2, 1, &image_size); char hash[33]; int result = rc_hash_generate_from_buffer(hash, RC_CONSOLE_NINTENDO, image, image_size); free(image); /* NOTE: expectation is that this hash matches the hash in test_hash_fds_two_sides */ ASSERT_NUM_EQUALS(result, 1); ASSERT_STR_EQUALS(hash, "fd770d4d34c00760fabda6ad294a8f0b"); ASSERT_NUM_EQUALS(image_size, 65500 * 2 + 16); } static void test_hash_nes_file_32k() { size_t image_size; uint8_t* image = generate_nes_file(32, 0, &image_size); char hash[33]; int result = hash_mock_file("test.nes", hash, RC_CONSOLE_NINTENDO, image, image_size); free(image); ASSERT_NUM_EQUALS(result, 1); ASSERT_STR_EQUALS(hash, "6a2305a2b6675a97ff792709be1ca857"); ASSERT_NUM_EQUALS(image_size, 32768); } static void test_hash_nes_iterator_32k() { size_t image_size; uint8_t* image = generate_nes_file(32, 0, &image_size); char hash1[33], hash2[33]; int result1, result2; struct rc_hash_iterator iterator; iterate_mock_file(&iterator, "test.nes", image, image_size); result1 = rc_hash_iterate(hash1, &iterator); result2 = rc_hash_iterate(hash2, &iterator); rc_hash_destroy_iterator(&iterator); free(image); ASSERT_NUM_EQUALS(result1, 1); ASSERT_STR_EQUALS(hash1, "6a2305a2b6675a97ff792709be1ca857"); ASSERT_NUM_EQUALS(result2, 0); ASSERT_STR_EQUALS(hash2, ""); } static void test_hash_nes_file_iterator_32k() { size_t image_size; uint8_t* image = generate_nes_file(32, 0, &image_size); char hash1[33], hash2[33]; int result1, result2; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "test.nes", image, image_size); result1 = rc_hash_iterate(hash1, &iterator); result2 = rc_hash_iterate(hash2, &iterator); rc_hash_destroy_iterator(&iterator); free(image); ASSERT_NUM_EQUALS(result1, 1); ASSERT_STR_EQUALS(hash1, "6a2305a2b6675a97ff792709be1ca857"); ASSERT_NUM_EQUALS(result2, 0); ASSERT_STR_EQUALS(hash2, ""); } /* ========================================================================= */ static void test_hash_n64(uint8_t* buffer, size_t buffer_size, const char* expected_hash) { char hash[33]; int result; rc_hash_reset_filereader(); /* explicitly unset the filereader */ result = rc_hash_generate_from_buffer(hash, RC_CONSOLE_NINTENDO_64, buffer, buffer_size); init_mock_filereader(); /* restore the mock filereader */ ASSERT_NUM_EQUALS(result, 1); ASSERT_STR_EQUALS(hash, expected_hash); } static void test_hash_n64_file(const char* filename, uint8_t* buffer, size_t buffer_size, const char* expected_hash) { char hash_file[33], hash_iterator[33]; mock_file(0, filename, buffer, buffer_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_NINTENDO_64, filename); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, filename, NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_hash); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_hash); } /* ========================================================================= */ static void test_hash_nds() { size_t image_size; uint8_t* image = generate_nds_file(2, 1234567, 654321, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_hash = "56b30c276cba4affa886bd38e8e34d7e"; mock_file(0, "game.nds", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_NINTENDO_DS, "game.nds"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.nds", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_hash); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_hash); } static void test_hash_nds_supercard() { size_t image_size, image2_size; uint8_t* image = generate_nds_file(2, 1234567, 654321, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_hash = "56b30c276cba4affa886bd38e8e34d7e"; ASSERT_PTR_NOT_NULL(image); ASSERT_NUM_GREATER(image_size, 0); /* inject the SuperCard header (512 bytes) */ image2_size = image_size + 512; uint8_t* image2 = malloc(image2_size); ASSERT_PTR_NOT_NULL(image2); memcpy(&image2[512], &image[0], image_size); memset(&image2[0], 0, 512); image2[0] = 0x2E; image2[1] = 0x00; image2[2] = 0x00; image2[3] = 0xEA; image2[0xB0] = 0x44; image2[0xB1] = 0x46; image2[0xB2] = 0x96; image2[0xB3] = 0x00; mock_file(0, "game.nds", image2, image2_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_NINTENDO_DS, "game.nds"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.nds", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); free(image2); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_hash); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_hash); } static void test_hash_nds_buffered() { size_t image_size; uint8_t* image = generate_nds_file(2, 1234567, 654321, &image_size); char hash_buffer[33], hash_iterator[33]; const char* expected_hash = "56b30c276cba4affa886bd38e8e34d7e"; /* test file hash */ int result_buffer = rc_hash_generate_from_buffer(hash_buffer, RC_CONSOLE_NINTENDO_DS, image, image_size); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.nds", image, image_size); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_buffer, 1); ASSERT_STR_EQUALS(hash_buffer, expected_hash); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_hash); } /* ========================================================================= */ static void test_hash_dsi() { size_t image_size; uint8_t* image = generate_nds_file(2, 1234567, 654321, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_hash = "56b30c276cba4affa886bd38e8e34d7e"; mock_file(0, "game.nds", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_NINTENDO_DSI, "game.nds"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.nds", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_hash); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_hash); } static void test_hash_dsi_buffered() { size_t image_size; uint8_t* image = generate_nds_file(2, 1234567, 654321, &image_size); char hash_buffer[33], hash_iterator[33]; const char* expected_hash = "56b30c276cba4affa886bd38e8e34d7e"; /* test file hash */ int result_buffer = rc_hash_generate_from_buffer(hash_buffer, RC_CONSOLE_NINTENDO_DSI, image, image_size); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.nds", image, image_size); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_buffer, 1); ASSERT_STR_EQUALS(hash_buffer, expected_hash); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_hash); } /* ========================================================================= */ static void test_hash_neogeocd() { const char* ipl_txt = "FIXA.FIX,0,0\r\nPROG.PRG,0,0\r\nSOUND.PCM,0,0\r\n\x1a"; const size_t prog_prg_size = 273470; uint8_t* prog_prg = generate_generic_file(prog_prg_size); size_t image_size; uint8_t* image = generate_iso9660_bin(160, "TEST", &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "96f35b20c6cf902286da45e81a50b2a3"; generate_iso9660_file(image, "IPL.TXT", (uint8_t*)ipl_txt, strlen(ipl_txt)); generate_iso9660_file(image, "PROG.PRG", prog_prg, prog_prg_size); mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_NEO_GEO_CD, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); free(prog_prg); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_neogeocd_multiple_prg() { const char* ipl_txt = "FIXA.FIX,0,0\r\nPROG1.PRG,0,0\r\nSOUND.PCM,0,0\r\nPROG2.PRG,0,44000\r\n\x1a"; const size_t prog1_prg_size = 273470; uint8_t* prog1_prg = generate_generic_file(prog1_prg_size); const size_t prog2_prg_size = 13768; uint8_t* prog2_prg = generate_generic_file(prog2_prg_size); size_t image_size; uint8_t* image = generate_iso9660_bin(160, "TEST", &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "d62df483c4786d3c63f27b6c5f17eeca"; generate_iso9660_file(image, "IPL.TXT", (uint8_t*)ipl_txt, strlen(ipl_txt)); generate_iso9660_file(image, "PROG1.PRG", prog1_prg, prog1_prg_size); generate_iso9660_file(image, "PROG2.PRG", prog2_prg, prog2_prg_size); mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_NEO_GEO_CD, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); free(prog1_prg); free(prog2_prg); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_neogeocd_lowercase_ipl_contents() { const char* ipl_txt = "fixa.fix,0,0\r\nprog.prg,0,0\r\nsound.pcm,0,0\r\n\x1a"; const size_t prog_prg_size = 273470; uint8_t* prog_prg = generate_generic_file(prog_prg_size); size_t image_size; uint8_t* image = generate_iso9660_bin(160, "TEST", &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "96f35b20c6cf902286da45e81a50b2a3"; generate_iso9660_file(image, "IPL.TXT", (uint8_t*)ipl_txt, strlen(ipl_txt)); generate_iso9660_file(image, "PROG.PRG", prog_prg, prog_prg_size); mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_NEO_GEO_CD, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); free(prog_prg); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } /* ========================================================================= */ static void test_hash_pce_cd() { size_t image_size; uint8_t* image = generate_pce_cd_bin(72, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "6565819195a49323e080e7539b54f251"; mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PC_ENGINE_CD, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_pce_cd_invalid_header() { size_t image_size; uint8_t* image = generate_pce_cd_bin(72, &image_size); mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* make the header not match */ image[2048 + 0x24] = 0x34; test_hash_unknown_format(RC_CONSOLE_PC_ENGINE_CD, "game.cue"); free(image); } /* ========================================================================= */ static void test_hash_pcfx() { size_t image_size; uint8_t* image = generate_pcfx_bin(72, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "0a03af66559b8529c50c4e7788379598"; mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PCFX, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_pcfx_invalid_header() { size_t image_size; uint8_t* image = generate_pcfx_bin(72, &image_size); mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* make the header not match */ image[12] = 0x34; test_hash_unknown_format(RC_CONSOLE_PCFX, "game.cue"); free(image); } static void test_hash_pcfx_pce_cd() { /* Battle Heat is formatted as a PC-Engine CD */ size_t image_size; uint8_t* image = generate_pce_cd_bin(72, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "6565819195a49323e080e7539b54f251"; mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); mock_file(2, "game2.bin", image, image_size); /* PC-Engine CD check only applies to track 2 */ /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PCFX, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_psx_cd() { /* BOOT=cdrom:\SLUS_007.45 */ size_t image_size; uint8_t* image = generate_psx_bin("SLUS_007.45", 0x07D800, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "db433fb038cde4fb15c144e8c7dea6e3"; mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PLAYSTATION, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_psx_cd_no_system_cnf() { size_t image_size; uint8_t* image; char hash_file[33], hash_iterator[33]; const char* expected_md5 = "e494c79a7315be0dc3e8571c45df162c"; uint32_t binary_size = 0x12000; const uint32_t sectors_needed = (((binary_size + 2047) / 2048) + 20); uint8_t* exe; image = generate_iso9660_bin(sectors_needed, "HOMEBREW", &image_size); exe = generate_iso9660_file(image, "PSX.EXE", NULL, binary_size); memcpy(exe, "PS-X EXE", 8); binary_size -= 2048; exe[28] = binary_size & 0xFF; exe[29] = (binary_size >> 8) & 0xFF; exe[30] = (binary_size >> 16) & 0xFF; exe[31] = (binary_size >> 24) & 0xFF; mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PLAYSTATION, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_psx_cd_exe_in_subfolder() { /* BOOT=cdrom:\bin\SLUS_012.37 */ size_t image_size; uint8_t* image = generate_psx_bin("bin\\SCES_012.37", 0x07D800, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "674018e23a4052113665dfb264e9c2fc"; mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PLAYSTATION, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_psx_cd_extra_slash() { /* BOOT=cdrom:\\SLUS_007.45 */ size_t image_size; uint8_t* image = generate_psx_bin("\\SLUS_007.45", 0x07D800, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "db433fb038cde4fb15c144e8c7dea6e3"; mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PLAYSTATION, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_ps2_iso() { size_t image_size; uint8_t* image = generate_ps2_bin("SLUS_200.64", 0x07D800, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "01a517e4ad72c6c2654d1b839be7579d"; mock_file(0, "game.iso", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PLAYSTATION_2, "game.iso"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.iso", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_ps2_psx() { size_t image_size; uint8_t* image = generate_psx_bin("SLUS_007.45", 0x07D800, &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "db433fb038cde4fb15c144e8c7dea6e3"; mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PLAYSTATION_2, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); /* PSX hash */ result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation (should not generate PS2 hash for PSX file) */ ASSERT_NUM_EQUALS(result_file, 0); ASSERT_NUM_EQUALS(result_iterator, 0); } static void test_hash_psp() { const size_t param_sfo_size = 690; uint8_t* param_sfo = generate_generic_file(param_sfo_size); const size_t eboot_bin_size = 273470; uint8_t* eboot_bin = generate_generic_file(eboot_bin_size); size_t image_size; uint8_t* image = generate_iso9660_bin(160, "TEST", &image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "27ec2f9b7238b2ef29af31ddd254f201"; generate_iso9660_file(image, "PSP_GAME\\PARAM.SFO", param_sfo, param_sfo_size); generate_iso9660_file(image, "PSP_GAME\\SYSDIR\\EBOOT.BIN", eboot_bin, eboot_bin_size); mock_file(0, "game.iso", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PSP, "game.iso"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.iso", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); free(eboot_bin); free(param_sfo); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_psp_video() { const size_t param_sfo_size = 690; uint8_t* param_sfo = generate_generic_file(param_sfo_size); const size_t eboot_bin_size = 273470; uint8_t* eboot_bin = generate_generic_file(eboot_bin_size); size_t image_size; uint8_t* image = generate_iso9660_bin(160, "TEST", &image_size); char hash_file[33], hash_iterator[33]; /* UMD video disc may have an UPDATE folder, but nothing in the PSP_GAME or SYSDIR folders. */ generate_iso9660_file(image, "PSP_GAME\\SYSDIR\\UPDATE\\EBOOT.BIN", eboot_bin, eboot_bin_size); /* the PARAM.SFO file is in the UMD_VIDEO folder. */ generate_iso9660_file(image, "UMD_VIDEO\\PARAM.SFO", param_sfo, param_sfo_size); mock_file(0, "game.iso", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PSP, "game.iso"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.iso", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); free(eboot_bin); free(param_sfo); /* validation */ ASSERT_NUM_EQUALS(result_file, 0); ASSERT_NUM_EQUALS(result_iterator, 0); } static void test_hash_psp_homebrew() { const size_t image_size = 3532124; uint8_t* image = generate_generic_file(image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "fcde8760893b09e508e5f4fe642eb132"; mock_file(0, "eboot.pbp", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PSP, "eboot.pbp"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "eboot.pbp", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_sega_cd() { /* the first 512 bytes of sector 0 are a volume header and ROM header. * generate a generic block and add the Sega CD marker */ size_t image_size = 512; uint8_t* image = generate_generic_file(image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "574498e1453cb8934df60c4ab906e783"; memcpy(image, "SEGADISCSYSTEM ", 16); mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_SEGA_CD, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_sega_cd_invalid_header() { size_t image_size = 512; uint8_t* image = generate_generic_file(image_size); mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); test_hash_unknown_format(RC_CONSOLE_SEGA_CD, "game.cue"); free(image); } static void test_hash_saturn() { /* the first 512 bytes of sector 0 are a volume header and ROM header. * generate a generic block and add the Sega CD marker */ size_t image_size = 512; uint8_t* image = generate_generic_file(image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "4cd9c8e41cd8d137be15bbe6a93ae1d8"; memcpy(image, "SEGA SEGASATURN ", 16); mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_SATURN, "game.cue"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cue", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_saturn_invalid_header() { size_t image_size = 512; uint8_t* image = generate_generic_file(image_size); mock_file(0, "game.bin", image, image_size); mock_file(1, "game.cue", (uint8_t*)"game.bin", 8); test_hash_unknown_format(RC_CONSOLE_SATURN, "game.cue"); free(image); } static void test_hash_scv_cart() { size_t image_size = 32768 + 32; uint8_t* image = generate_generic_file(image_size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "4309c9844b44f9ff8256dfc04687b8fd"; memcpy(image, "EmuSCV....CART..................", 32); mock_file(0, "game.cart", image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_SUPER_CASSETTEVISION, "game.cart"); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, "game.cart", NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } /* ========================================================================= */ static void assert_valid_m3u(const char* disc_filename, const char* m3u_filename, const char* m3u_contents) { const size_t size = 131072; uint8_t* image = generate_generic_file(size); char hash_file[33], hash_iterator[33]; const char* expected_md5 = "a0f425b23200568132ba76b2405e3933"; mock_file(0, disc_filename, image, size); mock_file(1, m3u_filename, (uint8_t*)m3u_contents, strlen(m3u_contents)); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PC8800, m3u_filename); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, m3u_filename, NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, expected_md5); ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_m3u_buffered() { const size_t size = 131072; uint8_t* image = generate_generic_file(size); char hash_iterator[33]; const char* m3u_filename = "test.m3u"; const char* filename = "test.d88"; const char* expected_md5 = "a0f425b23200568132ba76b2405e3933"; uint8_t* m3u_contents = (uint8_t*)filename; const size_t m3u_size = strlen(filename); mock_file(0, filename, image, size); mock_file(1, m3u_filename, m3u_contents, m3u_size); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, m3u_filename, m3u_contents, m3u_size); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, expected_md5); } static void test_hash_m3u_with_comments() { assert_valid_m3u("test.d88", "test.m3u", "#EXTM3U\r\n\r\n#EXTBYT:131072\r\ntest.d88\r\n"); } static void test_hash_m3u_empty() { char hash_file[33], hash_iterator[33]; const char* m3u_filename = "test.m3u"; const char* m3u_contents = "#EXTM3U\r\n\r\n#EXTBYT:131072\r\n"; mock_file(0, m3u_filename, (uint8_t*)m3u_contents, strlen(m3u_contents)); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_PC8800, m3u_filename); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, m3u_filename, NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* validation */ ASSERT_NUM_EQUALS(result_file, 0); ASSERT_NUM_EQUALS(result_iterator, 0); } static void test_hash_m3u_trailing_whitespace() { assert_valid_m3u("test.d88", "test.m3u", "#EXTM3U \r\n \r\n#EXTBYT:131072 \r\ntest.d88 \t \r\n"); } static void test_hash_m3u_line_ending() { assert_valid_m3u("test.d88", "test.m3u", "#EXTM3U\n\n#EXTBYT:131072\ntest.d88\n"); } static void test_hash_m3u_extension_case() { assert_valid_m3u("test.D88", "test.M3U", "#EXTM3U\r\n\r\n#EXTBYT:131072\r\ntest.D88\r\n"); } static void test_hash_m3u_relative_path() { assert_valid_m3u("folder1/folder2/test.d88", "folder1/test.m3u", "#EXTM3U\r\n\r\n#EXTBYT:131072\r\nfolder2/test.d88"); } static void test_hash_m3u_absolute_path(const char* absolute_path) { char m3u_contents[128]; snprintf(m3u_contents, sizeof(m3u_contents), "#EXTM3U\r\n\r\n#EXTBYT:131072\r\n%s", absolute_path); assert_valid_m3u(absolute_path, "relative/test.m3u", m3u_contents); } static void test_hash_file_without_ext() { size_t image_size; uint8_t* image = generate_nes_file(32, 1, &image_size); char hash_file[33], hash_iterator[33]; const char* filename = "test"; mock_file(0, filename, image, image_size); /* test file hash */ int result_file = rc_hash_generate_from_file(hash_file, RC_CONSOLE_NINTENDO, filename); /* test file identification from iterator */ int result_iterator; struct rc_hash_iterator iterator; rc_hash_initialize_iterator(&iterator, filename, NULL, 0); result_iterator = rc_hash_iterate(hash_iterator, &iterator); rc_hash_destroy_iterator(&iterator); /* cleanup */ free(image); /* validation */ /* specifying a console will use the appropriate hasher */ ASSERT_NUM_EQUALS(result_file, 1); ASSERT_STR_EQUALS(hash_file, "6a2305a2b6675a97ff792709be1ca857"); /* no extension will use the default full file iterator, so hash should include header */ ASSERT_NUM_EQUALS(result_iterator, 1); ASSERT_STR_EQUALS(hash_iterator, "64b131c5c7fec32985d9c99700babb7e"); } /* ========================================================================= */ void test_hash(void) { TEST_SUITE_BEGIN(); init_mock_filereader(); init_mock_cdreader(); /* 3DO */ TEST(test_hash_3do_bin); TEST(test_hash_3do_cue); TEST(test_hash_3do_iso); TEST(test_hash_3do_invalid_header); TEST(test_hash_3do_launchme_case_insensitive); TEST(test_hash_3do_no_launchme); TEST(test_hash_3do_long_directory); /* Amstrad CPC */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_AMSTRAD_PC, "test.dsk", 194816, "9d616e4ad3f16966f61422c57e22aadd"); TEST_PARAMS4(test_hash_m3u, RC_CONSOLE_AMSTRAD_PC, "test.dsk", 194816, "9d616e4ad3f16966f61422c57e22aadd"); /* Apple II */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_APPLE_II, "test.nib", 232960, "96e8d33bdc385fd494327d6e6791cbe4"); TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_APPLE_II, "test.dsk", 143360, "88be638f4d78b4072109e55f13e8a0ac"); TEST_PARAMS4(test_hash_m3u, RC_CONSOLE_APPLE_II, "test.dsk", 143360, "88be638f4d78b4072109e55f13e8a0ac"); /* Arduboy */ TEST(test_hash_arduboy); TEST(test_hash_arduboy_crlf); TEST(test_hash_arduboy_no_final_lf); /* Arcade */ TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "game.zip", "c8d46d341bea4fd5bff866a65ff8aea9"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "game.7z", "c8d46d341bea4fd5bff866a65ff8aea9"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/game.zip", "c8d46d341bea4fd5bff866a65ff8aea9"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "\\game.zip", "c8d46d341bea4fd5bff866a65ff8aea9"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "roms\\game.zip", "c8d46d341bea4fd5bff866a65ff8aea9"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "C:\\roms\\game.zip", "c8d46d341bea4fd5bff866a65ff8aea9"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/roms/game.zip", "c8d46d341bea4fd5bff866a65ff8aea9"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/games/game.zip", "c8d46d341bea4fd5bff866a65ff8aea9"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/roms/game.7z", "c8d46d341bea4fd5bff866a65ff8aea9"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/nes_game.zip", "9b7aad36b365712fc93728088de4c209"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/nes/game.zip", "9b7aad36b365712fc93728088de4c209"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "C:\\roms\\nes\\game.zip", "9b7aad36b365712fc93728088de4c209"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "C:\\roms\\NES\\game.zip", "9b7aad36b365712fc93728088de4c209"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "nes\\game.zip", "9b7aad36b365712fc93728088de4c209"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/snes/game.zip", "c8d46d341bea4fd5bff866a65ff8aea9"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/nes2/game.zip", "c8d46d341bea4fd5bff866a65ff8aea9"); /* we don't care that multiple aliases for the same system generate different hashes - the point is * that they don't generate the same hash as an actual arcade ROM with the same filename. */ TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/chf/game.zip", "6ef57f16562ea0c7f49d93853b313e32"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/channelf/game.zip", "7b6506637a0cc79bd1d24a43a34fa3b9"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/coleco/game.zip", "c546f63ae7de98add4b9f221a4749260"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/colecovision/game.zip", "47279207b94dbf2a45cb13efa56d685e"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/msx/game.zip", "59ab85f6b56324fd81b4e324b804c29f"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/msx1/game.zip", "33328d832dcb0854383cdd4a4565c459"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/pce/game.zip", "c414a783f3983bbe2e9e01d9d5320c7e"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/pcengine/game.zip", "49370c3cbe98bdcdce545c68379487db"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/sgx/game.zip", "db545ab29694bfda1010317d4bac83b8"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/supergrafx/game.zip", "5665c9ef4c2f6609d8e420c4d86ba692"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/tg16/game.zip", "8b6c5c2e54915be2cdba63973862e143"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/fds/game.zip", "c0c135a97e8c577cfdf9204823ff211f"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/gamegear/game.zip", "f6f471e952b8103032b723f57bdbe767"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/mastersystem/game.zip", "f4805afe0ff5647140a26bd0a1057373"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/sms/game.zip", "43f35f575dead94dd2f42f9caf69fe5a"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/megadriv/game.zip", "f99d0aaf12ba3eb6ced9878c76692c63"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/megadrive/game.zip", "73eb5d7034b382093b1d36414d9e84e4"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/genesis/game.zip", "b62f810c63e1cba7f5b7569643bec236"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/sg1000/game.zip", "e8f6c711c4371f09537b4f2a7a304d6c"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/spectrum/game.zip", "a5f62157b2617bd728c4b1bc885c29e9"); TEST_PARAMS3(test_hash_filename, RC_CONSOLE_ARCADE, "/home/user/ngp/game.zip", "d4133b74c4e57274ca514e27a370dcb6"); /* Arcadia 2001 */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_ARCADIA_2001, "test.bin", 4096, "572686c3a073162e4ec6eff86e6f6e3a"); /* Atari 2600 */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_ATARI_2600, "test.bin", 2048, "02c3f2fa186388ba8eede9147fb431c4"); /* Atari 7800 */ TEST(test_hash_atari_7800); TEST(test_hash_atari_7800_with_header); /* Atari Jaguar */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_ATARI_JAGUAR, "test.jag", 0x400000, "a247ec8a8c42e18fcb80702dfadac14b"); /* Atari Jaguar CD */ TEST(test_hash_atari_jaguar_cd); TEST(test_hash_atari_jaguar_cd_byteswapped); TEST(test_hash_atari_jaguar_cd_track3); TEST(test_hash_atari_jaguar_cd_no_header); TEST(test_hash_atari_jaguar_cd_no_sessions); TEST(test_hash_atari_jaguar_cd_homebrew); /* Colecovision */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_COLECOVISION, "test.col", 16384, "455f07d8500f3fabc54906737866167f"); /* Commodore 64 */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_COMMODORE_64, "test.nib", 327936, "e7767d32b23e3fa62c5a250a08caeba3"); TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_COMMODORE_64, "test.d64", 174848, "ecd5a8ef4e77f2e9469d9b6e891394f0"); TEST_PARAMS4(test_hash_m3u, RC_CONSOLE_COMMODORE_64, "test.d64", 174848, "ecd5a8ef4e77f2e9469d9b6e891394f0"); /* Dreamcast */ TEST(test_hash_dreamcast_single_bin); TEST(test_hash_dreamcast_split_bin); TEST(test_hash_dreamcast_cue); /* Elektor TV Games Computer */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER, "test.pgm", 4096, "572686c3a073162e4ec6eff86e6f6e3a"); TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_ELEKTOR_TV_GAMES_COMPUTER, "test.tvc", 1861, "37097124a29aff663432d049654a17dc"); /* Fairchild Channel F */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_FAIRCHILD_CHANNEL_F, "test.bin", 2048, "02c3f2fa186388ba8eede9147fb431c4"); TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_FAIRCHILD_CHANNEL_F, "test.chf", 2048, "02c3f2fa186388ba8eede9147fb431c4"); /* Gameboy */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_GAMEBOY, "test.gb", 131072, "a0f425b23200568132ba76b2405e3933"); /* Gameboy Color */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_GAMEBOY_COLOR, "test.gbc", 2097152, "cf86acf519625a25a17b1246975e90ae"); /* Gameboy Advance */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_GAMEBOY_COLOR, "test.gba", 4194304, "a247ec8a8c42e18fcb80702dfadac14b"); /* Gamecube */ TEST(test_hash_gamecube); /* Game Gear */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_GAME_GEAR, "test.gg", 524288, "68f0f13b598e0b66461bc578375c3888"); /* Intellivision */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_INTELLIVISION, "test.bin", 8192, "ce1127f881b40ce6a67ecefba50e2835"); /* Interton VC 4000 */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_INTERTON_VC_4000, "test.bin", 2048, "02c3f2fa186388ba8eede9147fb431c4"); /* Magnavox Odyssey 2 */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_MAGNAVOX_ODYSSEY2, "test.bin", 4096, "572686c3a073162e4ec6eff86e6f6e3a"); /* Master System */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_MASTER_SYSTEM, "test.sms", 131072, "a0f425b23200568132ba76b2405e3933"); /* Mega Drive */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_MEGA_DRIVE, "test.md", 1048576, "da9461b3b0f74becc3ccf6c2a094c516"); TEST_PARAMS4(test_hash_m3u, RC_CONSOLE_MEGA_DRIVE, "test.md", 1048576, "da9461b3b0f74becc3ccf6c2a094c516"); /* Mega Duck */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_MEGADUCK, "test.bin", 65536, "8e6576cd5c21e44e0bbfc4480577b040"); /* MSX */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_MSX, "test.dsk", 737280, "0e73fe94e5f2e2d8216926eae512b7a6"); TEST_PARAMS4(test_hash_m3u, RC_CONSOLE_MSX, "test.dsk", 737280, "0e73fe94e5f2e2d8216926eae512b7a6"); /* MS DOS */ TEST(test_hash_msdos_dosz); TEST(test_hash_msdos_dosz_zip64); TEST(test_hash_msdos_dosz_with_dosc); TEST(test_hash_msdos_dosz_with_parent); /* Neo Geo CD */ TEST(test_hash_neogeocd); TEST(test_hash_neogeocd_multiple_prg); TEST(test_hash_neogeocd_lowercase_ipl_contents); /* Neo Geo Pocket */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_NEOGEO_POCKET, "test.ngc", 2097152, "cf86acf519625a25a17b1246975e90ae"); /* NES */ TEST(test_hash_nes_32k); TEST(test_hash_nes_32k_with_header); TEST(test_hash_nes_256k); TEST(test_hash_fds_two_sides); TEST(test_hash_fds_two_sides_with_header); TEST(test_hash_nes_file_32k); TEST(test_hash_nes_file_iterator_32k); TEST(test_hash_nes_iterator_32k); /* Nintendo 64 */ TEST_PARAMS3(test_hash_n64, test_rom_z64, sizeof(test_rom_z64), "06096d7ce21cb6bcde38391534c4eb91"); TEST_PARAMS3(test_hash_n64, test_rom_v64, sizeof(test_rom_v64), "06096d7ce21cb6bcde38391534c4eb91"); TEST_PARAMS3(test_hash_n64, test_rom_n64, sizeof(test_rom_n64), "06096d7ce21cb6bcde38391534c4eb91"); TEST_PARAMS4(test_hash_n64_file, "game.z64", test_rom_z64, sizeof(test_rom_z64), "06096d7ce21cb6bcde38391534c4eb91"); TEST_PARAMS4(test_hash_n64_file, "game.v64", test_rom_v64, sizeof(test_rom_v64), "06096d7ce21cb6bcde38391534c4eb91"); TEST_PARAMS4(test_hash_n64_file, "game.n64", test_rom_n64, sizeof(test_rom_n64), "06096d7ce21cb6bcde38391534c4eb91"); TEST_PARAMS4(test_hash_n64_file, "game.n64", test_rom_z64, sizeof(test_rom_z64), "06096d7ce21cb6bcde38391534c4eb91"); /* misnamed */ TEST_PARAMS4(test_hash_n64_file, "game.z64", test_rom_n64, sizeof(test_rom_n64), "06096d7ce21cb6bcde38391534c4eb91"); /* misnamed */ TEST_PARAMS3(test_hash_n64, test_rom_ndd, sizeof(test_rom_ndd), "a698b32a52970d8a52a5a52c83acc2a9"); /* Nintendo DS */ TEST(test_hash_nds); TEST(test_hash_nds_supercard); TEST(test_hash_nds_buffered); /* Nintendo DSi */ TEST(test_hash_dsi); TEST(test_hash_dsi_buffered); /* Oric (no fixed file size) */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_ORIC, "test.tap", 18119, "953a2baa3232c63286aeae36b2172cef"); /* PC-8800 */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_PC8800, "test.d88", 348288, "8cca4121bf87200f45e91b905a9f5afd"); TEST_PARAMS4(test_hash_m3u, RC_CONSOLE_PC8800, "test.d88", 348288, "8cca4121bf87200f45e91b905a9f5afd"); /* PC Engine */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_PC_ENGINE, "test.pce", 524288, "68f0f13b598e0b66461bc578375c3888"); TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_PC_ENGINE, "test.pce", 524288 + 512, "258c93ebaca1c3f488ab48218e5e8d38"); /* PC Engine CD */ TEST(test_hash_pce_cd); TEST(test_hash_pce_cd_invalid_header); /* PC-FX */ TEST(test_hash_pcfx); TEST(test_hash_pcfx_invalid_header); TEST(test_hash_pcfx_pce_cd); /* Playstation */ TEST(test_hash_psx_cd); TEST(test_hash_psx_cd_no_system_cnf); TEST(test_hash_psx_cd_exe_in_subfolder); TEST(test_hash_psx_cd_extra_slash); /* Playstation 2 */ TEST(test_hash_ps2_iso); TEST(test_hash_ps2_psx); /* Playstation Portable */ TEST(test_hash_psp); TEST(test_hash_psp_video); TEST(test_hash_psp_homebrew); /* Pokemon Mini */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_POKEMON_MINI, "test.min", 524288, "68f0f13b598e0b66461bc578375c3888"); /* Sega CD */ TEST(test_hash_sega_cd); TEST(test_hash_sega_cd_invalid_header); /* Sega 32X */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_SEGA_32X, "test.bin", 3145728, "07d733f252896ec41b4fd521fe610e2c"); /* Sega Saturn */ TEST(test_hash_saturn); TEST(test_hash_saturn_invalid_header); /* SG-1000 */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_SG1000, "test.sg", 32768, "6a2305a2b6675a97ff792709be1ca857"); /* SNES */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_SUPER_NINTENDO, "test.smc", 524288, "68f0f13b598e0b66461bc578375c3888"); TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_SUPER_NINTENDO, "test.smc", 524288 + 512, "258c93ebaca1c3f488ab48218e5e8d38"); /* Super Cassette Vision */ TEST(test_hash_scv_cart); TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_SUPER_CASSETTEVISION, "test.bin", 32768, "6a2305a2b6675a97ff792709be1ca857"); /* TI-83 */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_TI83, "test.83g", 1695, "bfb6048395a425c69743900785987c42"); TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_TI83, "test.83p", 2500, "6e81d530ee9a79d4f4f505729ad74bb5"); /* TIC-80 */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_TIC80, "test.tic", 67682, "79b96f4ffcedb3ce8210a83b22cd2c69"); /* Uzebox */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_UZEBOX, "test.uze", 53654, "a9aab505e92edc034d3c732869159789"); /* Vectrex */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_SG1000, "test.vec", 4096, "572686c3a073162e4ec6eff86e6f6e3a"); /* VirtualBoy */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_SG1000, "test.vb", 524288, "68f0f13b598e0b66461bc578375c3888"); /* Watara Supervision */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_SUPERVISION, "test.sv", 32768, "6a2305a2b6675a97ff792709be1ca857"); /* WASM-4 */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_WASM4, "test.wasm", 33454, "bce38bb5f05622fc7e0e56757059d180"); /* WonderSwan */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_WONDERSWAN, "test.ws", 524288, "68f0f13b598e0b66461bc578375c3888"); TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_WONDERSWAN, "test.wsc", 4194304, "a247ec8a8c42e18fcb80702dfadac14b"); /* ZX Spectrum */ TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_ZX_SPECTRUM, "test.tap", 1596, "714a9f455e616813dd5421c5b347e5e5"); TEST_PARAMS4(test_hash_full_file, RC_CONSOLE_ZX_SPECTRUM, "test.tzx", 14971, "93723e6d1100f9d1d448a27cf6618c47"); /* m3u support */ TEST(test_hash_m3u_buffered); TEST(test_hash_m3u_with_comments); TEST(test_hash_m3u_empty); TEST(test_hash_m3u_trailing_whitespace); TEST(test_hash_m3u_line_ending); TEST(test_hash_m3u_extension_case); TEST(test_hash_m3u_relative_path); TEST_PARAMS1(test_hash_m3u_absolute_path, "/absolute/test.d88"); TEST_PARAMS1(test_hash_m3u_absolute_path, "\\absolute\\test.d88"); TEST_PARAMS1(test_hash_m3u_absolute_path, "C:\\absolute\\test.d88"); TEST_PARAMS1(test_hash_m3u_absolute_path, "\\\\server\\absolute\\test.d88"); TEST_PARAMS1(test_hash_m3u_absolute_path, "samba:/absolute/test.d88"); /* other */ TEST(test_hash_file_without_ext); TEST_SUITE_END(); }