X-Git-Url: https://git.jtryba.com/browse/friidump.git/blobdiff_plain/d44030dfa440b1967ec7dcd3ccfe46fb69934486..HEAD:/libfriidump/xbox_ref/utils.c?ds=inline diff --git a/libfriidump/xbox_ref/utils.c b/libfriidump/xbox_ref/utils.c index 2e24078..f437450 100644 --- a/libfriidump/xbox_ref/utils.c +++ b/libfriidump/xbox_ref/utils.c @@ -3,6 +3,7 @@ #include "xbe_cert.h" #include "unlock.h" #include "xbox_ref_log.h" +#include "../xbox_ref_bridge.h" #include #include @@ -32,6 +33,24 @@ #define GDR_8163B OL23 +/* The copied Xbox dumper is single-threaded. FriiDump installs one + * per-invocation cancellation callback before entering the copied path and + * clears it during cleanup. */ +static int (*g_xbox_dump_cancel)(void *cancel_data) = NULL; +static void *g_xbox_dump_cancel_data = NULL; +static xbox_ref_dump_result *g_xbox_dump_cancel_result = NULL; + +static BOOL XboxDumpCancelRequested(void) +{ + if (!g_xbox_dump_cancel || !g_xbox_dump_cancel(g_xbox_dump_cancel_data)) + return FALSE; + + if (g_xbox_dump_cancel_result) + g_xbox_dump_cancel_result->cancelled = 1; + + return TRUE; +} + HANDLE OpenDrive(char driveLetter) { char devicePath[16]; @@ -1231,23 +1250,6 @@ static uint32_t DetectXboxVolumeStart(HANDLE hDevice) return 0xFFFFFFFFu; } -static void RecoveryKick(HANDLE hDevice, BOOL authRecovery) -{ - unsigned char dummy[2048] = {0}; - - if (authRecovery) - KickXboxMediaAuth(hDevice); - - SetDriveSpeedMax(hDevice); - - for (int i = 0; i < 10; i++) - { - ScsiReadSectors(hDevice, 0, 1, dummy); - Sleep(50); - } -} - - static void GetDirectoryForPath(const char *filename, char *outDir, DWORD outDirSize) { DWORD len; @@ -1435,8 +1437,7 @@ static BOOL DumpSectorRangeWithRetry(HANDLE hDevice, uint32_t sourceStartLba, uint32_t sectorsToRead, uint32_t outputBaseLba, - const char *phaseName, - BOOL authRecovery) + const char *phaseName) { const uint32_t batchSize = 32; unsigned char *buffer = NULL; @@ -1458,11 +1459,19 @@ static BOOL DumpSectorRangeWithRetry(HANDLE hDevice, printf("\n--- STARTING %s RANGE ---\n", phaseName); printf("Source LBA: %u | Output LBA: %u | Sectors: %u\n", sourceStartLba, outputBaseLba, sectorsToRead); - RecoveryKick(hDevice, authRecovery); - while (sectorsDone < sectorsToRead) { - uint32_t currentLba = sourceStartLba + sectorsDone; + uint32_t currentLba; + + if (XboxDumpCancelRequested()) + { + printf("\n[CANCEL] User cancellation requested during %s at output LBA %u.\n", + phaseName, outputBaseLba + sectorsDone); + VirtualFree(buffer, 0, MEM_RELEASE); + return FALSE; + } + + currentLba = sourceStartLba + sectorsDone; const char *currentLayerStr = "L0"; uint32_t burstLimit = batchSize; uint32_t toRead; @@ -1491,6 +1500,14 @@ static BOOL DumpSectorRangeWithRetry(HANDLE hDevice, for (int retry = 0; retry <= MAX_RETRIES; retry++) { + if (XboxDumpCancelRequested()) + { + printf("\n[CANCEL] User cancellation requested during %s retry at output LBA %u.\n", + phaseName, outputBaseLba + sectorsDone); + VirtualFree(buffer, 0, MEM_RELEASE); + return FALSE; + } + if (ScsiReadSectors(hDevice, currentLba, (uint16_t)toRead, buffer)) { if (!WriteOutputBytes(outFile, buffer, (size_t)toRead * 2048U, phaseName, currentLba, outputBaseLba + sectorsDone)) @@ -1504,7 +1521,6 @@ static BOOL DumpSectorRangeWithRetry(HANDLE hDevice, break; } - RecoveryKick(hDevice, authRecovery); Sleep(500); } @@ -1527,6 +1543,15 @@ static BOOL DumpSectorRangeWithRetry(HANDLE hDevice, for (int sRetry = 0; sRetry <= MAX_RETRIES; sRetry++) { + if (XboxDumpCancelRequested()) + { + printf("\n[CANCEL] User cancellation requested during %s sector recovery at output LBA %u.\n", + phaseName, outputBaseLba + sectorsDone); + VirtualFree(smallBuffer, 0, MEM_RELEASE); + VirtualFree(buffer, 0, MEM_RELEASE); + return FALSE; + } + if (ScsiReadSectors(hDevice, currentLba + i, 1, smallBuffer)) { if (!WriteOutputBytes(outFile, smallBuffer, 2048, phaseName, currentLba + i, outputBaseLba + sectorsDone)) @@ -1541,7 +1566,6 @@ static BOOL DumpSectorRangeWithRetry(HANDLE hDevice, break; } - RecoveryKick(hDevice, authRecovery); Sleep(500); } @@ -1612,7 +1636,17 @@ static BOOL WriteZeroSectorsOutput(FILE *outFile, while (sectorsDone < sectorCount) { - uint32_t toWrite = (sectorCount - sectorsDone > batchSectors) ? batchSectors : (sectorCount - sectorsDone); + uint32_t toWrite; + + if (XboxDumpCancelRequested()) + { + printf("\n[CANCEL] User cancellation requested during %s at output LBA %u.\n", + phaseName ? phaseName : "ZERO", outputBaseLba + sectorsDone); + VirtualFree(zeroBuffer, 0, MEM_RELEASE); + return FALSE; + } + + toWrite = (sectorCount - sectorsDone > batchSectors) ? batchSectors : (sectorCount - sectorsDone); uint32_t outputLba = outputBaseLba + sectorsDone; if (!WriteOutputBytes(outFile, zeroBuffer, (size_t)toWrite * 2048U, phaseName ? phaseName : "ZERO", 0, outputLba)) @@ -1980,7 +2014,7 @@ static void JsonWriteValidationWarnings(FILE *f, const XboxDvdSidecarCapture *ca if (!payloadFilesPresent) WRITE_WARNING("payload_files_not_fully_present"); if (redumpStyleZeroFilledPadding) - WRITE_WARNING("redump_style_padding_zero_filled_pending_hardware_capture"); + WRITE_WARNING("redump_style_padding_zero_filled_unresolved_content"); #undef WRITE_WARNING @@ -2475,6 +2509,9 @@ static BOOL WriteXboxDvdMediaProfileFile(const char *isoFilename, fprintf(json, " \"pregame_padding_start_lba\": %u,\n", redumpStyle ? XGD1_VIDEO_L0_SECTORS : 0U); fprintf(json, " \"pregame_padding_sector_count\": %u,\n", redumpStyle ? (XGD1_GAME_OUTPUT_START_LBA - XGD1_VIDEO_L0_SECTORS) : 0U); fprintf(json, " \"game_output_start_lba\": %u,\n", gameOutputLba); + fprintf(json, " \"game_leadin_unlocked_source_start_lba\": %u,\n", redumpStyle ? 0U : gameSourceLba); + fprintf(json, " \"game_leadin_source_sector_count\": %u,\n", redumpStyle ? XGD1_XISO_LEADIN_SECTORS : 0U); + fprintf(json, " \"game_leadin_source\": "); JsonWriteEscapedString(json, redumpStyle ? "drive_read10" : "not_applicable"); fprintf(json, ",\n"); fprintf(json, " \"game_unlocked_source_start_lba\": %u,\n", gameSourceLba); fprintf(json, " \"game_unlocked_source_sector_count\": %u,\n", redumpStyle ? XGD1_GAME_SOURCE_SECTORS : gameSectors); fprintf(json, " \"game_xiso_leadin_sector_count\": %u,\n", redumpStyle ? XGD1_XISO_LEADIN_SECTORS : 0U); @@ -2493,10 +2530,12 @@ static BOOL WriteXboxDvdMediaProfileFile(const char *isoFilename, fprintf(json, " \"reconstruction\": {\n"); fprintf(json, " \"is_reconstructed_layout\": %s,\n", redumpStyle ? "true" : "false"); - fprintf(json, " \"filler_policy\": "); JsonWriteEscapedString(json, redumpStyle ? "zero_fill_until_drive_can_read_filler" : "not_applicable"); fprintf(json, ",\n"); + fprintf(json, " \"filler_policy\": "); JsonWriteEscapedString(json, redumpStyle ? "pregame_and_postgame_zero_fill_content_placeholder" : "not_applicable"); fprintf(json, ",\n"); + fprintf(json, " \"filler_geometry_verified\": %s,\n", redumpStyle ? "true" : "false"); fprintf(json, " \"filler_verified_from_disc\": false,\n"); fprintf(json, " \"filler_byte_value\": %s,\n", redumpStyle ? "0" : "null"); - fprintf(json, " \"pending_hardware_capture\": %s,\n", redumpStyle ? "true" : "false"); + fprintf(json, " \"pending_hardware_capture\": false,\n"); + fprintf(json, " \"unresolved_filler_content\": %s,\n", redumpStyle ? "true" : "false"); fprintf(json, " \"filler_ranges\": [\n"); if (redumpStyle) { @@ -2514,7 +2553,7 @@ static BOOL WriteXboxDvdMediaProfileFile(const char *isoFilename, fprintf(json, " }\n"); } fprintf(json, " ],\n"); - fprintf(json, " \"note\": \"Padding/filler ranges are intentionally zero-filled placeholders until a drive/workflow capable of reading those regions is available.\"\n"); + fprintf(json, " \"note\": \"Pregame and postgame physical locations are verified by cache-aligned raw sector IDs, but their inaccessible contents remain zero-filled placeholders. The 32-sector game lead-in is drive-captured from unlocked LBA 0..31.\"\n"); fprintf(json, " },\n"); fprintf(json, " \"dvd_structures\": {\n"); @@ -2627,6 +2666,9 @@ static BOOL WriteXboxDvdSidecarFiles(const char *isoFilename, fprintf(json, " \"pregame_padding_start_lba\": %u,\n", redumpStyle ? XGD1_VIDEO_L0_SECTORS : 0U); fprintf(json, " \"pregame_padding_sector_count\": %u,\n", redumpStyle ? (XGD1_GAME_OUTPUT_START_LBA - XGD1_VIDEO_L0_SECTORS) : 0U); fprintf(json, " \"game_output_start_lba\": %u,\n", gameOutputLba); + fprintf(json, " \"game_leadin_unlocked_source_start_lba\": %u,\n", redumpStyle ? 0U : gameSourceLba); + fprintf(json, " \"game_leadin_source_sector_count\": %u,\n", redumpStyle ? XGD1_XISO_LEADIN_SECTORS : 0U); + fprintf(json, " \"game_leadin_source\": "); JsonWriteEscapedString(json, redumpStyle ? "drive_read10" : "not_applicable"); fprintf(json, ",\n"); fprintf(json, " \"game_unlocked_source_start_lba\": %u,\n", gameSourceLba); fprintf(json, " \"game_unlocked_source_sector_count\": %u,\n", redumpStyle ? XGD1_GAME_SOURCE_SECTORS : gameSectors); fprintf(json, " \"game_xiso_leadin_sector_count\": %u,\n", redumpStyle ? XGD1_XISO_LEADIN_SECTORS : 0U); @@ -2695,7 +2737,10 @@ static BOOL WriteXboxDvdSidecarFiles(const char *isoFilename, fprintf(json, " ],\n"); fprintf(json, " \"auth\": {\n"); - fprintf(json, " \"requires_media_transition\": true,\n"); + fprintf(json, " \"requires_media_transition\": false,\n"); + fprintf(json, " \"unlock_requires_media_transition\": false,\n"); + fprintf(json, " \"locked_video_view_restore_requires_media_transition\": true,\n"); + fprintf(json, " \"state_detection\": \"READ CAPACITY (10) visible-sector count\",\n"); fprintf(json, " \"mode_page\": \"0x3E\",\n"); fprintf(json, " \"challenge_table_source\": \"read_dvd_structure_xbox_control_block\",\n"); fprintf(json, " \"challenge_table_response_offset\": 774,\n"); @@ -2734,7 +2779,12 @@ static BOOL WriteXboxDvdSidecarFiles(const char *isoFilename, return ok; } -void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uint32_t totalDiscSectors, bool isDualLayer, bool EjectOnSuccess) +BOOL DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, + uint32_t totalDiscSectors, bool isDualLayer, + bool EjectOnSuccess, + int (*cancel)(void *cancel_data), + void *cancel_data, + xbox_ref_dump_result *result) { HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; @@ -2760,20 +2810,36 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin memset(&sidecarCapture, 0, sizeof(sidecarCapture)); + g_xbox_dump_cancel = cancel; + g_xbox_dump_cancel_data = cancel_data; + g_xbox_dump_cancel_result = result; + + if (result) { + result->attempted = 1; + result->mode = xisoFormat; + if (filename && filename[0]) { + strncpy(result->output_path, filename, sizeof(result->output_path) - 1); + result->output_path[sizeof(result->output_path) - 1] = '\0'; + } + } + if (totalDiscSectors == 0) totalDiscSectors = GetTotalSectors(hDevice); if (totalDiscSectors == 0) totalDiscSectors = REDUMP_SECTORS; + if (XboxDumpCancelRequested()) + { + printf("\n[CANCEL] User cancellation requested before Xbox output preparation.\n"); + goto cleanup; + } + rawTargetSectors = NormalizeRawIsoTargetSectors(totalDiscSectors, isDualLayer); if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) - return; + goto cleanup; if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)) - { - CryptReleaseContext(hProv, 0); - return; - } + goto cleanup; EnsureDriveReady(hDevice, 30000); SetDriveSpeedMax(hDevice); @@ -2788,6 +2854,8 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin outputLabel = "RAW ISO"; expectedOutputBytes = (unsigned long long)rawTargetSectors * 2048ULL; + if (result) + result->output_sectors = rawTargetSectors; if (rawTargetSectors != totalDiscSectors) { @@ -2850,7 +2918,7 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin rawGameSectors = 0; CaptureUnlockedSidecarState(hDevice, &sidecarCapture); rawSidecarAvailable = TRUE; - dumpOk = DumpSectorRangeWithRetry(hDevice, outFile, hHash, 0, rawTargetSectors, 0, "RAW", FALSE); + dumpOk = DumpSectorRangeWithRetry(hDevice, outFile, hHash, 0, rawTargetSectors, 0, "RAW"); } else if (rawTargetSectors == XGD1_FULL_REDUMP_SECTORS) { @@ -2891,7 +2959,7 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin } printf("[RAW] Writing video L0 from locked source LBA 0..%u.\n", XGD1_VIDEO_L0_SECTORS - 1); - if (!DumpSectorRangeWithRetry(hDevice, outFile, hHash, 0, XGD1_VIDEO_L0_SECTORS, 0, "VIDEO-L0", FALSE)) + if (!DumpSectorRangeWithRetry(hDevice, outFile, hHash, 0, XGD1_VIDEO_L0_SECTORS, 0, "VIDEO-L0")) { VirtualFree(videoL1Buffer, 0, MEM_RELEASE); goto cleanup; @@ -2909,8 +2977,6 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin Sleep(2000); EnsureDriveReady(hDevice, 30000); SetDriveSpeedMax(hDevice); - KickXboxMediaAuth(hDevice); - RecoveryKick(hDevice, TRUE); CaptureUnlockedSidecarState(hDevice, &sidecarCapture); rawSidecarAvailable = TRUE; @@ -2926,20 +2992,27 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin printf("[WARN] XDFS was detected at unlocked source LBA %u, not the expected XISO header LBA 32.\n", detectedXdfsLba); } - // Build the Redump-style XISO/game region using the same proven - // convention as option 2: the first 32 sectors are synthetic XISO - // lead-in/padding, and the real XDFS volume begins at source LBA 32. - // Do not read unlocked source LBA 0..31 here; on the 8050L path those - // LBAs are not the XDFS header sectors we want in the rebuilt image. + // FriiDump 0.5.3.13 cache-aligned raw-ID validation proved that + // unlocked source LBA 0..31 is the physical 32-sector game-region + // lead-in and that XDVDFS begins at source LBA 32. Preserve the + // drive-captured lead-in instead of synthesizing zero sectors. gameSourceLba = XGD1_GAME_SOURCE_START_LBA; gameSectors = REDUMP_SECTORS; rawVideoSectors = XGD1_GAME_OUTPUT_START_LBA; rawGameSourceLba = gameSourceLba; rawGameSectors = gameSectors; - printf("[RAW] Writing %u-sector XISO lead-in/padding at output LBA %u.\n", - XGD1_XISO_LEADIN_SECTORS, XGD1_GAME_OUTPUT_START_LBA); - if (!WriteZeroSectorsOutput(outFile, hHash, XGD1_XISO_LEADIN_SECTORS, XGD1_GAME_OUTPUT_START_LBA, "GAME-XISO-LEADIN")) + printf("[RAW] Capturing %u-sector game lead-in from unlocked source LBA 0..%u at output LBA %u.\n", + XGD1_XISO_LEADIN_SECTORS, + XGD1_XISO_LEADIN_SECTORS - 1, + XGD1_GAME_OUTPUT_START_LBA); + if (!DumpSectorRangeWithRetry(hDevice, + outFile, + hHash, + 0, + XGD1_XISO_LEADIN_SECTORS, + XGD1_GAME_OUTPUT_START_LBA, + "GAME-XISO-LEADIN")) { VirtualFree(videoL1Buffer, 0, MEM_RELEASE); goto cleanup; @@ -2955,8 +3028,7 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin XGD1_GAME_SOURCE_START_LBA, XGD1_GAME_SOURCE_SECTORS, XGD1_GAME_OUTPUT_START_LBA + XGD1_XISO_LEADIN_SECTORS, - "GAME-XISO", - TRUE)) + "GAME-XISO")) { VirtualFree(videoL1Buffer, 0, MEM_RELEASE); goto cleanup; @@ -2984,7 +3056,7 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin rawVideoSectors = videoSectors; printf("[RAW] Dumping contiguous visible/video area first: source LBA 0..%u.\n", videoSectors - 1); - if (!DumpSectorRangeWithRetry(hDevice, outFile, hHash, 0, videoSectors, 0, "VIDEO", FALSE)) + if (!DumpSectorRangeWithRetry(hDevice, outFile, hHash, 0, videoSectors, 0, "VIDEO")) goto cleanup; printf("[RAW] Re-applying full Xbox handshake after media transition for hidden game/data area.\n"); @@ -2993,8 +3065,6 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin Sleep(2000); EnsureDriveReady(hDevice, 30000); SetDriveSpeedMax(hDevice); - KickXboxMediaAuth(hDevice); - RecoveryKick(hDevice, TRUE); CaptureUnlockedSidecarState(hDevice, &sidecarCapture); rawSidecarAvailable = TRUE; @@ -3010,7 +3080,7 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin rawGameSectors = gameSectors; printf("[RAW] Appending hidden game/data area from unlocked source LBA %u for %u sectors.\n", gameSourceLba, gameSectors); - dumpOk = DumpSectorRangeWithRetry(hDevice, outFile, hHash, gameSourceLba, gameSectors, videoSectors, "GAME", TRUE); + dumpOk = DumpSectorRangeWithRetry(hDevice, outFile, hHash, gameSourceLba, gameSectors, videoSectors, "GAME"); } } else if (xisoFormat == '2') @@ -3026,8 +3096,6 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin goto cleanup; SetDriveSpeedMax(hDevice); - KickXboxMediaAuth(hDevice); - RecoveryKick(hDevice, TRUE); vol = (XDFS_VOLUME_DESCRIPTOR *)sectorBuffer; @@ -3061,6 +3129,8 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin outputLabel = "XISO"; expectedOutputBytes = ((unsigned long long)sectorsToRead + 32ULL) * 2048ULL; + if (result) + result->output_sectors = sectorsToRead + 32U; printf("[SUCCESS] Final XISO target: %u sectors plus 32-sector lead-in (%llu bytes).\n", sectorsToRead, @@ -3087,12 +3157,18 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin printf("Writing 64KB XISO lead-in padding...\n"); for (int p = 0; p < 32; p++) { + if (XboxDumpCancelRequested()) + { + printf("\n[CANCEL] User cancellation requested during XISO lead-in padding at output LBA %d.\n", p); + goto cleanup; + } + if (!WriteOutputBytes(outFile, zeroSector, 2048, "XISO-PAD", 0, (uint32_t)p)) goto cleanup; CryptHashData(hHash, zeroSector, 2048, 0); } - dumpOk = DumpSectorRangeWithRetry(hDevice, outFile, hHash, startLba, sectorsToRead, 32, "XISO", TRUE); + dumpOk = DumpSectorRangeWithRetry(hDevice, outFile, hHash, startLba, sectorsToRead, 32, "XISO"); } else { @@ -3150,6 +3226,19 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin printf("\n[WARN] Could not calculate CRC32/MD5/SHA-1/SHA-256 from finalized output file.\n"); } + if (result) { + result->output_size = GetFileSizeBytes64(filename); + strncpy(result->crc32, crc32String, sizeof(result->crc32) - 1); + result->crc32[sizeof(result->crc32) - 1] = '\0'; + strncpy(result->md5, md5String, sizeof(result->md5) - 1); + result->md5[sizeof(result->md5) - 1] = '\0'; + strncpy(result->sha1, sha1String, sizeof(result->sha1) - 1); + result->sha1[sizeof(result->sha1) - 1] = '\0'; + strncpy(result->sha256, fileSha256String, sizeof(result->sha256) - 1); + result->sha256[sizeof(result->sha256) - 1] = '\0'; + result->hashes_complete = result->crc32[0] && result->md5[0] && result->sha1[0] && result->sha256[0]; + } + if (xisoFormat == '1') { printf("Final RAW ISO Sector Count: %u\n", rawTargetSectors); @@ -3192,16 +3281,78 @@ void DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uin FormatElapsedTime(GetTickCount() - operationStartTick, operationTimeStr); printf("\nOperation Complete! Total elapsed: %s\n", operationTimeStr); - StopDriveUnit(hDevice); - + /* The caller owns final drive cleanup. Embedded FriiDump runs issue one + * STOP UNIT after Redump verification; standalone bridge runs stop the + * drive in xbox_ref_gdr8050l_dump_core() before closing the handle. */ if (EjectOnSuccess) ControlTray(hDevice, TRUE); cleanup: if (outFile) - fclose(outFile); + { + if (result && result->cancelled) + { + if (!FlushAndCommitOutput(outFile, outputLabel)) + printf("\n[WARN] Could not fully flush the controlled partial %s output before hashing.\n", + outputLabel ? outputLabel : "Xbox"); + } + if (fclose(outFile) != 0) + { + printf("\n[WARN] fclose failed for partial %s output.\n", + outputLabel ? outputLabel : "Xbox"); + if (errno) + printf(" errno: %d (%s)\n", errno, strerror(errno)); + } + outFile = NULL; + } if (hHash) CryptDestroyHash(hHash); if (hProv) CryptReleaseContext(hProv, 0); + if (result) { + result->dump_success = dumpOk ? 1 : 0; + result->elapsed_seconds = (double)(GetTickCount() - operationStartTick) / 1000.0; + if (result->output_size == 0 && filename && filename[0]) + result->output_size = GetFileSizeBytes64(filename); + result->completed_sectors = (uint32_t)(result->output_size / 2048ULL); + + if (result->cancelled && + result->output_size > 0 && + !result->hashes_complete) + { + printf("\n[HASH] Finalizing controlled partial Xbox output hashes...\n"); + if (CalculateFileHashes(filename, + crc32String, sizeof(crc32String), + md5String, sizeof(md5String), + fileSha1String, sizeof(fileSha1String), + fileSha256String, sizeof(fileSha256String))) + { + strncpy(result->crc32, crc32String, sizeof(result->crc32) - 1); + result->crc32[sizeof(result->crc32) - 1] = '\0'; + strncpy(result->md5, md5String, sizeof(result->md5) - 1); + result->md5[sizeof(result->md5) - 1] = '\0'; + strncpy(result->sha1, fileSha1String, sizeof(result->sha1) - 1); + result->sha1[sizeof(result->sha1) - 1] = '\0'; + strncpy(result->sha256, fileSha256String, sizeof(result->sha256) - 1); + result->sha256[sizeof(result->sha256) - 1] = '\0'; + result->hashes_complete = + result->crc32[0] && + result->md5[0] && + result->sha1[0] && + result->sha256[0]; + if (result->hashes_complete) + printf("[OK] Controlled partial Xbox output hashes captured.\n"); + } + else + { + printf("[WARN] Controlled partial Xbox output hashing failed.\n"); + } + } + } + + g_xbox_dump_cancel = NULL; + g_xbox_dump_cancel_data = NULL; + g_xbox_dump_cancel_result = NULL; + + return dumpOk; }