From dd85bf7e4f564e74cee515e6f4a55d9d9cda6129 Mon Sep 17 00:00:00 2001 From: Yutaka Sawada <60930312+Yutaka-Sawada@users.noreply.github.com> Date: Sun, 12 Mar 2023 11:02:25 +0900 Subject: [PATCH] Add files via upload --- source/par2j/par2.c | 1110 ++++++++++++++ source/par2j/par2.h | 42 + source/par2j/par2_cmd.c | 2193 +++++++++++++++++++++++++++ source/par2j/par2j.vcxproj | 165 ++ source/par2j/phmd5.c | 109 ++ source/par2j/phmd5.h | 66 + source/par2j/phmd5a.c | 480 ++++++ source/par2j/phmd5s.c | 277 ++++ source/par2j/reedsolomon.c | 816 ++++++++++ source/par2j/reedsolomon.h | 80 + source/par2j/repair.c | 879 +++++++++++ source/par2j/repair.h | 71 + source/par2j/res_par2j.rc | 26 + source/par2j/rs_decode.c | 2186 +++++++++++++++++++++++++++ source/par2j/rs_decode.h | 57 + source/par2j/rs_encode.c | 2292 ++++++++++++++++++++++++++++ source/par2j/rs_encode.h | 72 + source/par2j/search.c | 1243 +++++++++++++++ source/par2j/search.h | 42 + source/par2j/source.cl | 187 +++ source/par2j/verify.c | 2931 ++++++++++++++++++++++++++++++++++++ source/par2j/verify.h | 63 + source/par2j/version.h | 2 + 23 files changed, 15389 insertions(+) create mode 100644 source/par2j/par2.c create mode 100644 source/par2j/par2.h create mode 100644 source/par2j/par2_cmd.c create mode 100644 source/par2j/par2j.vcxproj create mode 100644 source/par2j/phmd5.c create mode 100644 source/par2j/phmd5.h create mode 100644 source/par2j/phmd5a.c create mode 100644 source/par2j/phmd5s.c create mode 100644 source/par2j/reedsolomon.c create mode 100644 source/par2j/reedsolomon.h create mode 100644 source/par2j/repair.c create mode 100644 source/par2j/repair.h create mode 100644 source/par2j/res_par2j.rc create mode 100644 source/par2j/rs_decode.c create mode 100644 source/par2j/rs_decode.h create mode 100644 source/par2j/rs_encode.c create mode 100644 source/par2j/rs_encode.h create mode 100644 source/par2j/search.c create mode 100644 source/par2j/search.h create mode 100644 source/par2j/source.cl create mode 100644 source/par2j/verify.c create mode 100644 source/par2j/verify.h create mode 100644 source/par2j/version.h diff --git a/source/par2j/par2.c b/source/par2j/par2.c new file mode 100644 index 0000000..fb77326 --- /dev/null +++ b/source/par2j/par2.c @@ -0,0 +1,1110 @@ +// par2.c +// Copyright : 2023-03-10 Yutaka Sawada +// License : GPL + +#ifndef _UNICODE +#define _UNICODE +#endif +#ifndef UNICODE +#define UNICODE +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 // Windows Vista or later +#endif + +#include +#include + +#include +#include + +#include "common2.h" +#include "par2.h" +#include "crc.h" +#include "create.h" +#include "search.h" +#include "list.h" +#include "verify.h" +#include "repair.h" +#include "ini.h" +#include "reedsolomon.h" + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// パリティを作成する +int par2_create( + wchar_t *uni_buf, // 作業用、入力されたコメントが入ってる + int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限数 + int block_distri, // パリティ・ブロックの分配方法 + int switch_p) // インデックス・ファイルを作らない, ユニコードのファイル名も記録する +{ + unsigned char *tmp_p, *common_buf = NULL, *footer_buf; + int err = 0, i, packet_num, common_size, footer_size; + HANDLE *rcv_hFile = NULL; + file_ctx_c *files = NULL; + source_ctx_c *s_blk = NULL; + parity_ctx_c *p_blk = NULL; + + init_crc_table(); // CRC 計算用のテーブルを作成する + + // ソース・ファイルの情報 + files = (file_ctx_c *)malloc(sizeof(file_ctx_c) * file_num); + if (files == NULL){ + printf("malloc, %zd\n", sizeof(file_ctx_c) * file_num); + err = 1; + goto error_end; + } + + // ソース・ファイルの情報を集める + if (err = get_source_files(files)) + goto error_end; + + // ソース・ブロック番号ごとに、どこから読み込むのかを設定する + s_blk = (source_ctx_c *)malloc(sizeof(source_ctx_c) * source_num); + if (s_blk == NULL){ + printf("malloc, %zd\n", sizeof(source_ctx_c) * source_num); + err = 1; + goto error_end; + } + common_size = 0; + for (i = 0; i < entity_num; i++){ // recovery set 内の順番でブロックを割り当てる + footer_size = (int)(files[i].size / (__int64)block_size); + while (footer_size > 0){ // フルサイズのブロック + s_blk[common_size].file = i; + s_blk[common_size].size = block_size; + common_size++; + footer_size--; + } + footer_size = (int)(files[i].size % (__int64)block_size); + if (footer_size > 0){ // 半端なブロック + s_blk[common_size].file = i; + s_blk[common_size].size = footer_size; + common_size++; + } + } + + // パケットを格納するバッファー + common_size = 64 + 12 + (file_num * 16); // Main packet + common_size += ((64 + 56 + 3) * file_num) + (list_len * 3); // File Description packet + common_size += ((64 + 16) * entity_num) + (source_num * 20); // Input File Slice Checksum packet + if ((switch_p & 2) != 0) + common_size += ((64 + 16 + 2) * file_num) + (list_len * 2); // Unicode Filename packet + common_size *= 2; // 2倍確保する + common_size += 64 + 12; // Creator packet "par2j v*.*.*" + if (uni_buf[0] != 0){ + //common_size += 64 + 3 + wcslen(uni_buf); // ASCII Comment packet + common_size += 64 + 16 + 2 + (int)(wcslen(uni_buf) * 2); // Unicode Comment packet + } + common_buf = (unsigned char *)malloc(common_size); + if (common_buf == NULL){ + printf("malloc, %d\n", common_size); + err = 1; + goto error_end; + } + printf("\n"); + + // 1-pass方式が可能かどうかを先に判定する + if (parity_num == 0){ // パリティ・ブロックを作らない場合 + err = -10; + } else if (source_num <= 1){ // ソース・ブロックが一個だけなら + err = -11; + } else if (memory_use & 16){ // SSDなら1-pass方式を使わない + err = -12; + } else { + // メモリーを確保できるか試す + err = read_block_num(parity_num, cpu_num - 1, 0, 256); + if (err == 0) + err = -13; + } +#ifdef TIMER + printf("read_block_num = %d\n", read_block_num(parity_num, cpu_num - 1, 0, 256)); +#endif + if (err > 0){ // 1-pass方式が可能 +#ifdef TIMER + printf("1-pass processing is possible, %d\n", err); +#endif + err = 0; + + // ファイルのハッシュ値とブロックのチェックサムを計算せずに、共通パケットを作成する + common_size = set_common_packet_1pass(common_buf, &packet_num, (switch_p & 2) >> 1, files); + if (common_size <= 2){ + err = common_size; + goto error_end; + } + // 末尾パケットを作成する + footer_buf = common_buf + (common_size * 2); // 共通パケットの後 + footer_size = set_footer_packet(footer_buf, uni_buf, common_buf + 32); + //printf("packet_num = %d, common_size = %d, footer_size = %d\n", packet_num, common_size, footer_size); + // 大きいめに確保しておいて、サイズ確定後に縮小する + tmp_p = (unsigned char *)realloc(common_buf, common_size * 2 + footer_size); + if (tmp_p == NULL){ + printf("realloc, %d\n", common_size * 2 + footer_size); + err = 1; + goto error_end; + } else { + common_buf = tmp_p; + footer_buf = tmp_p + (common_size * 2); // 共通パケットの後 + } + // 同じ Set ID の記録があれば消去しておく + if (recent_data != 0) + reset_ini_file(common_buf + 32); + + // 書庫ファイルに連結するためには、ファイル・ハンドルが必要となる + if (split_size == 1){ + rcv_hFile = (HANDLE *)calloc(recovery_num, sizeof(HANDLE)); + if (rcv_hFile == NULL){ + printf("calloc, %zd\n", sizeof(HANDLE) * recovery_num); + err = 1; + goto error_end; + } + } + + // パリティ・ブロックを作成する + uni_buf[MAX_LEN - 1] = switch_p & 1; // インデックス・ファイルを作るかどうか + err = rs_encode_1pass(ini_path, uni_buf, packet_limit, block_distri, packet_num, + common_buf, common_size, footer_buf, footer_size, rcv_hFile, files, s_blk); + } + // 2-pass方式で続行する + if (err < 0){ +#ifdef TIMER + printf("2-pass processing is selected, %d\n", err); +#endif + if (err > -10){ // 作成済みのパケットは作り直さない + // ハッシュ値だけ計算する + if (err = set_common_packet_hash(common_buf, files)) + goto error_end; + memcpy(common_buf + common_size, common_buf, common_size); // 後の半分に前半のをコピーする + //printf("packet_num = %d, common_size = %d, footer_size = %d\n", packet_num, common_size, footer_size); + if (rcv_hFile){ + free(rcv_hFile); + rcv_hFile = NULL; + } + } else { + // 共通パケットを作成する + if ((memory_use & 16) && (cpu_num >= 4) && (entity_num >= 2)){ // SSDなら複数ファイルを同時に処理する + common_size = set_common_packet_multi(common_buf, &packet_num, (switch_p & 2) >> 1, files); + } else { + common_size = set_common_packet(common_buf, &packet_num, (switch_p & 2) >> 1, files); + } + if (common_size <= 2){ + err = common_size; + goto error_end; + } + memcpy(common_buf + common_size, common_buf, common_size); // 後の半分に前半のをコピーする + // 末尾パケットを作成する + footer_buf = common_buf + (common_size * 2); // 共通パケットの後 + footer_size = set_footer_packet(footer_buf, uni_buf, common_buf + 32); + //printf("packet_num = %d, common_size = %d, footer_size = %d\n", packet_num, common_size, footer_size); + // 大きいめに確保しておいて、サイズ確定後に縮小する + tmp_p = (unsigned char *)realloc(common_buf, common_size * 2 + footer_size); + if (tmp_p == NULL){ + printf("realloc, %d\n", common_size * 2 + footer_size); + err = 1; + goto error_end; + } else { + common_buf = tmp_p; + footer_buf = tmp_p + (common_size * 2); // 共通パケットの後 + } + // 同じ Set ID の記録があれば消去しておく + if (recent_data != 0) + reset_ini_file(common_buf + 32); + } + err = 0; + + // 既に完全なインデックス・ファイルが存在するなら作成しない +/* if (GetFileAttributes(recovery_file) != INVALID_FILE_ATTRIBUTES){ + hFile = CreateFile(recovery_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile != INVALID_HANDLE_VALUE){ + if (GetFileSize(hFile, NULL) == common_size + footer_size){ + unsigned int crc, crc2; + // 既存のインデックス・ファイルの CRC-32 を求める + crc = file_crc_part(hFile); + crc2 = crc_update(0xFFFFFFFF, common_buf, common_size); + crc2 = crc_update(crc2, footer_buf, footer_size) ^ 0xFFFFFFFF; + //printf("CRC = %08X, %08X\n", crc, crc2); + if (crc == crc2){ + switch_p |= 1; + printf("index file already exists\n"); + } + } + CloseHandle(hFile); + } + }*/ + + if ((switch_p & 1) == 0){ // インデックス・ファイルを作るときだけ + HANDLE hFile; + print_progress_text(0, "Making index file"); + // パリティ・ブロックを含まないリカバリ・ファイルを書き込む + //move_away_file(recovery_file); // 既存のファイルをどかす + hFile = CreateFile(recovery_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + if (GetLastError() == ERROR_PATH_NOT_FOUND){ // Path not found (3) + make_dir(recovery_file); // 途中のフォルダが存在しないのなら作成する + hFile = CreateFile(recovery_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + printf_cp("cannot create file, %s\n", recovery_file); + err = 1; + goto error_end; + } + } + if (!WriteFile(hFile, common_buf, common_size, &i, NULL)){ + print_win32_err(); + CloseHandle(hFile); + err = 1; + goto error_end; + } + if (!WriteFile(hFile, footer_buf, footer_size, &i, NULL)){ + print_win32_err(); + CloseHandle(hFile); + err = 1; + goto error_end; + } + CloseHandle(hFile); + print_progress_done(); + } + if (parity_num == 0) + goto creation_end; // パリティ・ブロックを作らない場合は一気に分割へ跳ぶ + + // パリティ・ブロックの領域を確保する + p_blk = (parity_ctx_c *)malloc(sizeof(parity_ctx_c) * parity_num); + if (p_blk == NULL){ + printf("malloc, %zd\n", sizeof(parity_ctx_c) * parity_num); + err = 1; + goto error_end; + } + rcv_hFile = (HANDLE *)calloc(recovery_num, sizeof(HANDLE)); + if (rcv_hFile == NULL){ + printf("calloc, %zd\n", sizeof(HANDLE) * recovery_num); + err = 1; + goto error_end; + } + + // リカバリ・ファイルを作成して共通パケットをコピーする + err = create_recovery_file(uni_buf, packet_limit, block_distri, + packet_num, common_buf, common_size, footer_buf, footer_size, rcv_hFile, p_blk); + if (err){ + delete_recovery_file(uni_buf, block_distri, switch_p & 1, rcv_hFile); + goto error_end; + } + // Recovery Slice packet 用のパケット・ヘッダーを作成しておく + set_packet_header((unsigned char *)uni_buf, common_buf + 32, 4, block_size + 4); + free(common_buf); + common_buf = NULL; + + // パリティ・ブロックを作成する + print_progress_text(0, "Creating recovery slice"); + err = rs_encode(ini_path, (unsigned char *)uni_buf, rcv_hFile, files, s_blk, p_blk); + } + if (err){ + delete_recovery_file(uni_buf, block_distri, switch_p & 1, rcv_hFile); + goto error_end; + } + +creation_end: + // ソース・ファイルを分割する + if (split_size >= 4){ + if (err = split_files(files, &common_size, &footer_size)){ + delete_split_files(files, common_size, footer_size); + goto error_end; + } + // ソース・ファイルの書庫にリカバリ・レコードを追加する + } else if (split_size == 1){ + if (err = append_recovery_file(list_buf + files[0].name, rcv_hFile, switch_p & 1)) + goto error_end; + } + + printf("\nCreated successfully\n"); +error_end: + if (common_buf) + free(common_buf); + if (s_blk) + free(s_blk); + if (p_blk) + free(p_blk); + if (rcv_hFile){ + for (i = 0; i < recovery_num; i++){ + if (rcv_hFile[i]) + CloseHandle(rcv_hFile[i]); + } + free(rcv_hFile); + } + if (files) + free(files); + return err; +} + +// リカバリ・ファイルの構成を試算する +int par2_trial( + wchar_t *uni_buf, // 作業用、入力されたコメントが入ってる + int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限数 + int block_distri, // パリティ・ブロックの分配方法 + int switch_p) // インデックス・ファイルを作らない, ユニコードのファイル名も記録する +{ + int packet_num, common_size, footer_size; + __int64 total_data_size; + double rate1, rate2; + + // ソース・ファイルの情報から共通パケットのサイズを計算する + common_size = measure_common_packet(&packet_num, (switch_p & 2) >> 1); + if (common_size <= 2) + return common_size; + + // 末尾パケットのサイズを計算する + footer_size = measure_footer_packet(uni_buf); + + // リカバリ・ファイルのサイズを計算する + printf("\nPAR File count\t: %d\n", recovery_num + 1 - (switch_p & 1)); + printf(" Size Packet Slice : Filename\n"); + fflush(stdout); + total_data_size = total_file_size; + measure_recovery_file(uni_buf, packet_limit, block_distri, packet_num, common_size, footer_size, switch_p & 1); + printf("\nPAR File total size\t: %I64d\n\n", total_file_size); + + // 効率を計算する + if ((source_num == 0) || (total_file_size == 0)){ + rate1 = 0; + rate2 = 0; + } else { + rate1 = (double)total_data_size / ((double)block_size * (double)source_num); + rate2 = (double)block_size * (double)parity_num / (double)total_file_size; + } + printf("File data in Blocks\t: %.1lf%%\n", rate1 * 100); + printf("Blocks in PAR files\t: %.1lf%%\n", rate2 * 100); + printf("Efficiency rate\t\t: %.1lf%%\n", rate1 * rate2 * 100); + + printf("\nTrial end\n"); + return 0; +} + +// ソース・ファイルの破損や欠損を調べる +int par2_verify( + wchar_t *uni_buf) // 作業用 +{ + char ascii_buf[MAX_LEN * 3]; + unsigned char set_id[16], *work_buf = NULL; + int err = 0, i, j, need_repair, parity_now, recovery_lost; + file_ctx_r *files = NULL; + source_ctx_r *s_blk = NULL; + parity_ctx_r *p_blk = NULL; + + if (switch_v & 4) // 順列検査なら追加検査を無視して簡易検査にする + switch_v = (switch_v & ~2) | 1; + init_crc_table(); // CRC 計算用のテーブルを作成する + + // リカバリ・ファイルを検索する + if (search_recovery_files() != 0){ + err = 1; + goto error_end; + } + + // Main packet を探す (パケット・サイズは SEARCH_SIZE * 2 まで) + work_buf = (unsigned char *)malloc(SEARCH_SIZE * 3); + if (work_buf == NULL){ + printf("malloc, %d\n", SEARCH_SIZE * 3); + err = 1; + goto error_end; + } + if (err = search_main_packet(work_buf, set_id)){ + printf("valid file is not found\n"); + goto error_end; + } + + // ソース・ファイルの情報 + files = (file_ctx_r *)malloc(sizeof(file_ctx_r) * file_num); + if (files == NULL){ + printf("malloc, %zd\n", sizeof(file_ctx_r) * file_num); + err = 1; + goto error_end; + } + // Main packet から File ID を読み取る + for (i = 0; i < file_num; i++){ + memcpy(files[i].id, work_buf + (16 * i), 16); + files[i].name = -1; + } + onepass_window_gen(block_size); // ブロック単位でずらして検査するための CRC テーブルを作る + + i = check_ini_file(set_id); // 検査するかどうか + if (i != 0){ // 検査済みなら記録を読み込む + if (read_ini_file(uni_buf, files)) + i = 0; // 記録を読み込めなかった + } + if (i == 0){ // 検査する + // ファイル情報のパケットを探す + if (err = search_file_packet(ascii_buf, work_buf, uni_buf, set_id, 1, files)) + goto error_end; + write_ini_file(files); // ファイル情報を記録しておく + } + if (err = set_file_data(ascii_buf, files)) // ソース・ファイル情報を確認して集計する + goto error_end; + + // ソース・ブロックの情報 + s_blk = (source_ctx_r *)malloc(sizeof(source_ctx_r) * source_num); + if (s_blk == NULL){ + printf("malloc, %zd\n", sizeof(source_ctx_r) * source_num); + err = 1; + goto error_end; + } + for (i = 0; i < entity_num; i++){ + // ファイルごとにソース・ブロックの情報を設定する + j = files[i].b_off; // そのファイル内のブロックの開始番号 + parity_now = (int)(files[i].size / (__int64)block_size); + while (parity_now > 0){ // フルサイズのブロック + s_blk[j].file = i; + s_blk[j].size = block_size; + s_blk[j].exist = 0; + j++; + parity_now--; + } + parity_now = (int)(files[i].size % (__int64)block_size); + if (parity_now > 0){ // 半端なブロック + s_blk[j].file = i; + s_blk[j].size = parity_now; + s_blk[j].exist = 0; + j++; + } + } + if (parity_num > 0){ + p_blk = (parity_ctx_r *)malloc(sizeof(parity_ctx_r) * parity_num); + if (p_blk == NULL){ + printf("malloc, %zd\n", sizeof(parity_ctx_r) * parity_num); + err = 1; + goto error_end; + } + for (i = 0; i < parity_num; i++) + p_blk[i].exist = 0; + } + + // 修復用のパケットを探す + recovery_lost = search_recovery_packet(ascii_buf, work_buf, uni_buf, set_id, NULL, files, s_blk, p_blk); + if (recovery_lost < 0){ + err = -recovery_lost; + goto error_end; + } + free(work_buf); + work_buf = NULL; + // チェックサムが揃ってるかを確かめる + for (i = 0; i < entity_num; i++){ + if (files[i].state & 0x80){ + //printf_cp("missing checksum, %s\n", list_buf + files[i].name); + break; + } + } + if (i == entity_num){ + write_ini_checksum(files, s_blk); // チェックサムを記録しておく + } else { + if (read_ini_checksum(files, s_blk)){ // チェックサムが記録されてるなら読み込む + update_ini_checksum(files, s_blk); // 存在するチェックサムだけ読み書きする + printf("\nInput File Slice Checksum packet is missing\n"); + } + } + + parity_now = first_num; // 利用可能なパリティ・ブロックの数 + j = 0; + for (i = 0; i < parity_num; i++){ + if (p_blk[i].exist != 0) + j = i + 1; // 利用できる最大値 + } + if ((j > 0) && (j < parity_num)){ // ブロックが少ないなら + // 使った分までに縮小する + parity_ctx_r *tmp_p_blk; + tmp_p_blk = (parity_ctx_r *)realloc(p_blk, sizeof(parity_ctx_r) * j); + if (tmp_p_blk != NULL) + p_blk = tmp_p_blk; + parity_num = j; // 本来のパリティ・ブロック数として扱う + } + if (parity_now == 0){ // パリティが無ければ + parity_num = 0; + free(p_blk); + p_blk = NULL; + } + printf("\nRecovery Slice count\t: %d\n", parity_num); + printf("Recovery Slice found\t: %d\n", parity_now); + + // ソース・ファイルが完全かどうかを調べる + // ファイルの状態は 完全、消失、追加、破損(完全なブロックの数) の4種類 + if ((memory_use & 16) && (cpu_num >= 4) && (entity_num >= 2)){ // SSDなら複数ファイルを同時に処理する + err = check_file_complete_multi(ascii_buf, uni_buf, files, s_blk); + } else { + err = check_file_complete(ascii_buf, uni_buf, files, s_blk); + } + if (err) + goto error_end; + + // ソース・ファイルが不完全なら別名・移動ファイルを探す + if (err = search_misnamed_file(ascii_buf, uni_buf, files, s_blk)) + goto error_end; + + // 破損・分割・類似名のファイルから使えるスライスを探す + if (err = search_file_slice(ascii_buf, uni_buf, files, s_blk)) + goto error_end; + + // 検査が終わったらメモリーを解放する + free(recv_buf); + recv_buf = NULL; + if (switch_b & 16){ + recovery_lost *= -1; // 表示しないようにマイナスにする + } else if (recv2_buf){ + free(recv2_buf); + recv2_buf = NULL; + } + if (list2_buf){ + free(list2_buf); + list2_buf = NULL; + } + + // ソース・ブロックを比較して、利用可能なブロックを増やす + if (first_num < source_num) + search_calculable_slice(files, s_blk); + + // 検査結果を集計する + err = result_file_state(ascii_buf, &need_repair, parity_now, recovery_lost, files, s_blk); + + // ソースファイルが完全ならリカバリファイルを削除する + if ((need_repair == 0) && ((switch_b & 16) != 0)) + purge_recovery_file(); + +error_end: + close_ini_file(); + if (recv_buf) + free(recv_buf); + if (recv2_buf) + free(recv2_buf); + if (s_blk) + free(s_blk); + if (p_blk) + free(p_blk); + if (files) + free(files); + return err; +} + +// ソース・ファイルの破損や欠損を修復する +int par2_repair( + wchar_t *uni_buf) // 作業用 +{ + char ascii_buf[MAX_LEN * 3]; + unsigned char set_id[16], *work_buf = NULL; + int err = 0, i, j, need_repair, recovery_lost; + int parity_now, lost_num, block_count; + HANDLE *rcv_hFile = NULL; + file_ctx_r *files = NULL; + source_ctx_r *s_blk = NULL; + parity_ctx_r *p_blk = NULL; + + switch_v |= 16; // 検査後に保存する + if (switch_v & 4){ // 順列検査なら追加検査を無視して簡易検査にする + switch_v = (switch_v & ~2) | 1; + switch_b = 0; // バックアップ設定を無効にする + } + init_crc_table(); // CRC 計算用のテーブルを作成する + + // リカバリ・ファイルを検索する + if (search_recovery_files() != 0){ + err = 1; + goto error_end; + } + + // Main packet を探す (パケット・サイズは SEARCH_SIZE * 2 まで) + work_buf = (unsigned char *)malloc(SEARCH_SIZE * 3); + if (work_buf == NULL){ + printf("malloc, %d\n", SEARCH_SIZE * 3); + err = 1; + goto error_end; + } + if (err = search_main_packet(work_buf, set_id)){ + printf("valid file is not found\n"); + goto error_end; + } + + // ソース・ファイルの情報 + files = (file_ctx_r *)malloc(sizeof(file_ctx_r) * file_num); + if (files == NULL){ + printf("malloc, %zd\n", sizeof(file_ctx_r) * file_num); + err = 1; + goto error_end; + } + // Main packet から File ID を読み取る + for (i = 0; i < file_num; i++){ + memcpy(files[i].id, work_buf + (16 * i), 16); + files[i].name = -1; + } + onepass_window_gen(block_size); // ブロック単位でずらして検査するための CRC テーブルを作る + + i = check_ini_file(set_id); // 検査するかどうか + if (i != 0){ // 検査済みなら記録を読み込む + if (read_ini_file(uni_buf, files)) + i = 0; // 記録を読み込めなかった + } + if (i == 0){ // 検査する + // ファイル情報のパケットを探す + if (err = search_file_packet(ascii_buf, work_buf, uni_buf, set_id, 1, files)) + goto error_end; + write_ini_file(files); // ファイル情報を記録しておく + } + if (err = set_file_data(ascii_buf, files)) // ソース・ファイル情報を確認して集計する + goto error_end; + + // リカバリ・ファイルのハンドル + rcv_hFile = (HANDLE *)calloc(recovery_num, sizeof(HANDLE)); + if (rcv_hFile == NULL){ + printf("calloc, %zd\n", sizeof(HANDLE) * recovery_num); + err = 1; + goto error_end; + } + // ソース・ブロックの情報 + s_blk = (source_ctx_r *)malloc(sizeof(source_ctx_r) * source_num); + if (s_blk == NULL){ + printf("malloc, %zd\n", sizeof(source_ctx_r) * source_num); + err = 1; + goto error_end; + } + for (i = 0; i < entity_num; i++){ + // ファイルごとにソース・ブロックの情報を設定する + j = files[i].b_off; // そのファイル内のブロックの開始番号 + parity_now = (int)(files[i].size / (__int64)block_size); + while (parity_now > 0){ // フルサイズのブロック + s_blk[j].file = i; + s_blk[j].size = block_size; + s_blk[j].exist = 0; + j++; + parity_now--; + } + parity_now = (int)(files[i].size % (__int64)block_size); + if (parity_now > 0){ // 半端なブロック + s_blk[j].file = i; + s_blk[j].size = parity_now; + s_blk[j].exist = 0; + j++; + } + } + if (parity_num > 0){ + p_blk = (parity_ctx_r *)malloc(sizeof(parity_ctx_r) * parity_num); + if (p_blk == NULL){ + printf("malloc, %zd\n", sizeof(parity_ctx_r) * parity_num); + err = 1; + goto error_end; + } + for (i = 0; i < parity_num; i++) + p_blk[i].exist = 0; + } + + // 修復用のパケットを探す + recovery_lost = search_recovery_packet(ascii_buf, work_buf, uni_buf, set_id, rcv_hFile, files, s_blk, p_blk); + if (recovery_lost < 0){ + err = -recovery_lost; + goto error_end; + } + free(work_buf); + work_buf = NULL; + // チェックサムが揃ってるかを確かめる + for (i = 0; i < entity_num; i++){ + if (files[i].state & 0x80) + break; + } + if (i == entity_num){ + write_ini_checksum(files, s_blk); // チェックサムを記録しておく + } else { + if (read_ini_checksum(files, s_blk)){ // チェックサムが記録されてるなら読み込む + update_ini_checksum(files, s_blk); // 存在するチェックサムだけ読み書きする + printf("\nInput File Slice Checksum packet is missing\n"); + } + } + + parity_now = first_num; // 利用可能なパリティ・ブロックの数 + j = 0; + for (i = 0; i < parity_num; i++){ + if (p_blk[i].exist != 0) + j = i + 1; // 利用できる最大値 + } + if ((j > 0) && (j < parity_num)){ // ブロックが少ないなら + // 使った分までに縮小する + parity_ctx_r *tmp_p_blk; + tmp_p_blk = (parity_ctx_r *)realloc(p_blk, sizeof(parity_ctx_r) * j); + if (tmp_p_blk != NULL) + p_blk = tmp_p_blk; + parity_num = j; // 本来のパリティ・ブロック数として扱う + } + if (parity_now == 0){ // パリティが無ければ + parity_num = 0; + free(p_blk); + p_blk = NULL; + } + printf("\nRecovery Slice count\t: %d\n", parity_num); + printf("Recovery Slice found\t: %d\n", parity_now); + + // ソース・ファイルが完全かどうかを一覧表示する + // ファイルの状態は 完全、消失、追加、破損(完全なブロックの数) の4種類 + if ((memory_use & 16) && (cpu_num >= 4) && (entity_num >= 2)){ // SSDなら複数ファイルを同時に処理する + err = check_file_complete_multi(ascii_buf, uni_buf, files, s_blk); + } else { + err = check_file_complete(ascii_buf, uni_buf, files, s_blk); + } + if (err) + goto error_end; + + // ソース・ファイルが不完全なら別名・移動ファイルを探す + if (err = search_misnamed_file(ascii_buf, uni_buf, files, s_blk)) + goto error_end; + + // 消失・破損ファイルがあるなら、その作業ファイルを作成する + wcscpy(uni_buf, base_dir); + for (i = 0; i < entity_num; i++){ + if (files[i].size == 0) + continue; + if (((files[i].state & 3) != 0) && ((files[i].state & 4) == 0)){ + // 作業用のソース・ファイルを作る + get_temp_name(list_buf + files[i].name, uni_buf + base_len); + if (create_temp_file(uni_buf, files[i].size)){ + printf_cp("cannot create file, %s\n", uni_buf); + err = 1; + goto error_end; + } + } + } + + // 破損・分割・類似名のファイルから使えるスライスを探す + if (err = search_file_slice(ascii_buf, uni_buf, files, s_blk)) + goto error_end; + + // 検査が終わったらメモリーを解放する + free(recv_buf); + recv_buf = NULL; + if (switch_b & 16){ + recovery_lost *= -1; // 表示しないようにマイナスにする + } else if (recv2_buf){ + free(recv2_buf); + recv2_buf = NULL; + } + if (list2_buf){ + free(list2_buf); + list2_buf = NULL; + } + + // ソース・ブロックを比較して、利用可能なブロックを増やす + block_count = 0; // 逆算可能なブロック数 + if (first_num < source_num) + block_count = search_calculable_slice(files, s_blk); + + // 検査結果を集計する + err = result_file_state(ascii_buf, &need_repair, parity_now, recovery_lost, files, s_blk); + lost_num = source_num - first_num; + if (need_repair == 0){ + if (switch_b & 16){ + if (rcv_hFile){ // 削除前にリカバリ・ファイルを閉じる + for (i = 0; i < recovery_num; i++){ + if (rcv_hFile[i]) + CloseHandle(rcv_hFile[i]); + } + free(rcv_hFile); + rcv_hFile = NULL; + } + purge_recovery_file(); // ソースファイルが完全ならリカバリファイルを削除する + } + goto error_end; // 全て完全なので修復する必要なし + } else if ((lost_num > parity_now) && ((need_repair & 0x2FFFFFFF) == 0)){ + if (switch_b & 4) + replace_incomplete(uni_buf, ascii_buf, files, s_blk); // 再構築したファイルで置き換える + goto error_end; // ブロック不足で、簡易修復や再構築ができるファイルも無い + } else if (need_repair == 0x40000000){ + goto error_end; // non-recovery set のファイルだけが消失・破損してる + } + if (need_repair & 0x20000000) // 消失・破損したファイルのスライスが全て利用可能な場合 + block_count |= 0x20000000; // 修復後の確認検査が必要な印 + +/* + // 修復するかどうかを入力してもらう + printf(" continue ? [Yes/No] : "); + i = _getche(); + printf("\n"); + if ((i != 'y') && (i != 'Y')) + goto error_end; +*/ + + // パリティ・ブロックの数が修復に必要な量よりも多すぎるなら最大値を調節する + // 逆行列の計算に失敗した時に別のパリティ・ブロックを使えるように +3個は残しておく + if ((lost_num > 0) && (parity_now > lost_num + 3)){ + parity_ctx_r *tmp_p_blk; + int max_num = 0; + j = parity_num; + for (i = 0; i < j; i++){ + if (p_blk[i].exist != 0){ + max_num++; + if (max_num > lost_num + 3){ + max_num--; + p_blk[i].exist = 0; + } else { + parity_num = i + 1; + } + } + } + tmp_p_blk = (parity_ctx_r *)realloc(p_blk, sizeof(parity_ctx_r) * parity_num); + if (tmp_p_blk != NULL) + p_blk = tmp_p_blk; + } + + // ブロックを復元しないのなら、先にリカバリ・ファイルを閉じる + if (lost_num == 0){ + for (i = 0; i < recovery_num; i++){ + if (rcv_hFile[i]){ + CloseHandle(rcv_hFile[i]); + rcv_hFile[i] = NULL; + } + } + free(rcv_hFile); + rcv_hFile = NULL; + } + + // 簡単な修復を先に行う、まだ修復の必要なファイルの数が戻る + need_repair = simple_repair(ascii_buf, need_repair & 0x0FFFFFFF, files); + + // ブロック単位で復元することができない、または必要が無いならここで終わる + if ((((block_count & 0x20000000) == 0) && (lost_num > parity_now)) + || ((lost_num == 0) && (need_repair == 0))){ + if (switch_b & 4) + replace_incomplete(uni_buf, ascii_buf, files, s_blk); // 再構築したファイルで置き換える + goto repair_end; + } + block_count &= 0x0FFFFFFF; + + if ((lost_num > 0) || (block_count > 0)) + printf("\nRepairing file :\n"); + if (block_size == 4){ // 破損したソース・ファイルを作り直す + if (err = restore_block4(uni_buf, files, s_blk)) + goto error_end; + } else if (block_count > 0){ // 同じブロックを流用する、または逆算する + if (err = restore_block(uni_buf, block_count, files, s_blk)) + goto error_end; + } + + if ((lost_num > 0) && (lost_num <= parity_now)){ // 失われたブロックを復元する + err = rs_decode(uni_buf, lost_num, rcv_hFile, files, s_blk, p_blk); + if (err) + goto error_end; + } + + if (rcv_hFile){ // 検査前にリカバリ・ファイルを閉じる + for (i = 0; i < recovery_num; i++){ + if (rcv_hFile[i]) + CloseHandle(rcv_hFile[i]); + } + free(rcv_hFile); + rcv_hFile = NULL; + } + + // 正しく修復できたか調べて結果表示する + printf("\nVerifying repair: %d\n", need_repair); + printf(" Status : Filename\n"); + fflush(stdout); + if (err = verify_repair(uni_buf, ascii_buf, files, s_blk)) + goto error_end; + +repair_end: + err = 16; + lost_num = 0; + for (i = 0; i < file_num; i++){ + if (files[i].state & 0x3F) + lost_num++; + } + if (lost_num == 0){ // 全て修復できたなら + printf("\nRepaired successfully\n"); + need_repair = 0; + } else { + printf("\nFailed to repair %d file(s)\n", lost_num); + err |= 4; + } + if (recovery_lost > 0) + err |= 256; + + // ソースファイルを全て復元できたらリカバリファイルを削除する + if ((need_repair == 0) && ((switch_b & 16) != 0)) + purge_recovery_file(); + +error_end: + close_ini_file(); + if (recv_buf) + free(recv_buf); + if (recv2_buf) + free(recv2_buf); + if (s_blk) + free(s_blk); + if (p_blk) + free(p_blk); + if (rcv_hFile){ + for (i = 0; i < recovery_num; i++){ + if (rcv_hFile[i]) + CloseHandle(rcv_hFile[i]); + } + free(rcv_hFile); + } + if (files){ + if (need_repair) + delete_work_file(uni_buf, files); + free(files); + } + return err; +} + +// ソース・ファイルの一覧を表示する +int par2_list( + wchar_t *uni_buf, // 作業用 + int switch_h) // ハッシュ値も表示する +{ + char ascii_buf[MAX_LEN * 3]; + unsigned char set_id[16], *work_buf = NULL; + int err = 0, i, file_block; + file_ctx_r *files = NULL; + + if (switch_v & 7) // 簡易検査、追加検査、順列検査の設定を無効にする + switch_v &= ~7; + + // Main packet を探す (対応する Main packet のサイズは SEARCH_SIZE * 2 まで) + work_buf = (unsigned char *)malloc(SEARCH_SIZE * 3); + if (work_buf == NULL){ + printf("malloc, %d\n", SEARCH_SIZE * 3); + err = 1; + goto error_end; + } + recv_buf = recovery_file; // 指定されたリカバリ・ファイルだけ調べる + recv_len = (int)wcslen(recovery_file); + recovery_num = 1; + if (err = search_main_packet(work_buf, set_id)){ + printf("valid file is not found\n"); + goto error_end; + } + + // ソース・ファイルの情報 + files = (file_ctx_r *)malloc(sizeof(file_ctx_r) * file_num); + if (files == NULL){ + printf("malloc, %zd\n", sizeof(file_ctx_r) * file_num); + err = 1; + goto error_end; + } + // Main packet から File ID を読み取る + for (i = 0; i < file_num; i++){ + memcpy(files[i].id, work_buf + (16 * i), 16); + files[i].name = -2; + } + + // ファイル情報のパケットを探す + if (err = search_file_packet(ascii_buf, work_buf, uni_buf, set_id, 0, files)) + goto error_end; + free(work_buf); + work_buf = NULL; + + // ソース・ファイルの一覧を表示する + printf("\nInput File list\t:\n"); + if (switch_h){ + printf(" Size Slice MD5 Hash : Filename\n"); + } else { + printf(" Size Slice : Filename\n"); + } + for (i = 0; i < file_num; i++){ + if (files[i].name < 0){ // ファイル情報が無くても処理を継続する + if (switch_h){ + printf(" ? ? ? : Unknown\n"); + } else { + printf(" ? ? : Unknown\n"); + } + err |= 4; + continue; + } + utf16_to_cp(list_buf + files[i].name, ascii_buf, cp_output); + if (files[i].size > 0){ + total_file_size += files[i].size; + if (i < entity_num){ + file_block = (int)((files[i].size + (__int64)block_size - 1) / (__int64)block_size); // ソース・ブロックの数 + source_num += file_block; + } else { + file_block = 0; + } + if (switch_h){ + printf("%13I64d %6d ", files[i].size, file_block); + print_hash(files[i].hash); + printf(" : \"%s\"\n", ascii_buf); + } else { + printf("%13I64d %6d : \"%s\"\n", files[i].size, file_block, ascii_buf); + } + } else { // 空のファイルやフォルダ + if (switch_h){ + printf(" 0 0 : \"%s\"\n", ascii_buf); + } else { + printf(" 0 0 : \"%s\"\n", ascii_buf); + } + } + } + + printf("\nInput File total size\t: %I64d\n", total_file_size); + printf("Input File Slice count\t: %d\n", source_num); + if (err == 0){ + printf("\nListed successfully\n"); + } else { + printf("\nFile Description packet is missing\n"); + } + +error_end: + if (work_buf) + free(work_buf); + if (files) + free(files); + return err; +} + +// CRC-32 チェックサムを使って自分自身の破損を検出する +int par2_checksum(wchar_t *uni_buf) // 作業用 +{ + unsigned int rv, crc, chk, chk2; + unsigned char *pAddr; + HANDLE hFile, hMap; + + init_crc_table(); // CRC 計算用のテーブルを作成する + + // 実行ファイルのパスを取得する + rv = GetModuleFileName(NULL, uni_buf, MAX_LEN); + if ((rv == 0) || (rv >= MAX_LEN)) + return 1; + //printf("%S\n", uni_buf); + + // 実行ファイルの PE checksum と CRC-32 を検証する + hFile = CreateFile(uni_buf, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + return 1; + } + rv = GetFileSize(hFile, &chk2); + if (rv == INVALID_FILE_SIZE) + return 1; + hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, chk2, rv, NULL); + if (hMap == NULL){ + CloseHandle(hFile); + return 1; + } + pAddr = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, rv); + if (pAddr == NULL){ + CloseHandle(hMap); + CloseHandle(hFile); + return 1; + } + if (CheckSumMappedFile(pAddr, rv, &chk2, &chk) == NULL){ // PE checksum + UnmapViewOfFile(pAddr); + CloseHandle(hMap); + CloseHandle(hFile); + return 1; + } + crc = crc_update_std(0xFFFFFFFF, pAddr, rv) ^ 0xFFFFFFFF; // CRC-32 + UnmapViewOfFile(pAddr); + CloseHandle(hMap); + CloseHandle(hFile); + + if (chk != chk2) + return 2; +#ifndef _WIN64 + if (crc != 0x22222222) +#else + if (crc != 0x22222A64) +#endif + return 3; + return 0; +} + diff --git a/source/par2j/par2.h b/source/par2j/par2.h new file mode 100644 index 0000000..3368a2d --- /dev/null +++ b/source/par2j/par2.h @@ -0,0 +1,42 @@ +#ifndef _PAR2_H_ +#define _PAR2_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +// パリティを作成する +int par2_create( + wchar_t *uni_buf, // 作業用、入力されたコメントが入ってる + int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限数 + int block_distri, // パリティ・ブロックの分配方法 + int switch_p); // インデックス・ファイルを作らない, ユニコードのファイル名も記録する + +// リカバリ・ファイルの構成を試算する +int par2_trial( + wchar_t *uni_buf, // 作業用、入力されたコメントが入ってる + int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限数 + int block_distri, // パリティ・ブロックの分配方法 + int switch_p); // インデックス・ファイルを作らない, ユニコードのファイル名も記録する + +// ソース・ファイルの破損や欠損を調べる +int par2_verify(wchar_t *uni_buf); // 作業用 + +// ソース・ファイルの破損や欠損を修復する +int par2_repair(wchar_t *uni_buf); // 作業用 + +// ソース・ファイルの一覧を表示する +int par2_list( + wchar_t *uni_buf, // 作業用 + int switch_h); // ハッシュ値も表示する + +// CRC-32 チェックサムを使って自分自身の破損を検出する +int par2_checksum(wchar_t *uni_buf); // 作業用 + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par2j/par2_cmd.c b/source/par2j/par2_cmd.c new file mode 100644 index 0000000..e7d9d37 --- /dev/null +++ b/source/par2j/par2_cmd.c @@ -0,0 +1,2193 @@ +// par2_cmd.c +// Copyright : 2022-10-15 Yutaka Sawada +// License : GPL + +#ifndef _UNICODE +#define _UNICODE +#endif +#ifndef UNICODE +#define UNICODE +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 // Windows Vista or later +#endif + +#include + +#include + +#include "common2.h" +#include "par2.h" +#include "ini.h" +#include "json.h" +#include "lib_opencl.h" +#include "version.h" + + +static void print_help(void) +{ + printf( +"Usage\n" +"t(rial) [options] [input files]\n" +"c(reate) [options] [input files]\n" +" available: f,fu,fo,fa,fe,ss,sn,sr,sm,rr,rn,rp,rs,rd,rf,ri,\n" +"\t lr,lp,ls,lc,m,vs,vd,c,d,in,up,uo\n" +"v(erify) [options] [external files]\n" +"r(epair) [options] [external files]\n" +" available: f,fu,fo,lc,m,vl,vs,vd,d,uo,w,b,br,bi\n" +"l(ist) [uo,h ] \n" +"\nOption\n" +" /f : Use file-list instead of filename\n" +" /fu : Use file-list which is encoded with UTF-8\n" +" /fo : Search file only for wildcard\n" +" /fa\"*\": Adding file at search with wildcard\n" +" /fe\"*\": Excluding file at search with wildcard\n" +" /ss: Slice size\n" +" /sn: Number of source blocks\n" +" /sr: Rate of source block count and size\n" +" /sm: Slice size becomes a multiple of this value\n" +" /rr: Rate of redundancy (%%)\n" +" /rn: Number of recovery blocks\n" +" /rp: Number of possible recovered files\n" +" /rs: Starting recovery block number\n" +" /rd: How to distribute recovery blocks to recovery files\n" +" /rf: Number of recovery files\n" +" /ri : Use file index to name recovery files\n" +" /lr: Limit number of recovery blocks in a recovery file\n" +" /lp: Limit repetition of packets in a recovery file\n" +" /ls: Limit size of splited files\n" +" /lc: Limit CPU feature\n" +" /m : Memory usage\n" +" /vl: Verification level\n" +" /vs: Skip verification by recent result\n" +" /vd\"*\": Set directory of recent result\n" +" /c\"*\" : Set comment\n" +" /d\"*\" : Set directory of input files\n" +" /in : Do not create index file\n" +" /up : Write Unicode Filename packet for non-ASCII filename\n" +" /uo : Console output is encoded with UTF-8\n" +" /w : Write information on JSON file\n" +" /p : Purge recovery files when input files are complete\n" +" /b : Backup existing files at repair\n" +" /br : Send existing files into recycle bin at repair\n" +" /bi : Replace files even when repair was failed\n" +" /h : List hash value of input files\n" + ); +} + +// 動作環境の表示 +static void print_environment(void) +{ + MEMORYSTATUSEX statex; + + // 「\\?\」は常に付加されてるので表示する際には無視する + printf_cp("Base Directory\t: \"%s\"\n", base_dir); + printf_cp("Recovery File\t: \"%s\"\n", recovery_file); + + printf("CPU thread\t: %d / %d\n", cpu_num & 0xFFFF, cpu_num >> 24); + cpu_num &= 0xFFFF; // 利用するコア数だけにしておく + printf("CPU cache limit : %d KB, %d KB\n", (cpu_cache & 0x7FFF8000) >> 10, (cpu_cache & 0x00007FFF) << 7); +#ifndef _WIN64 // 32-bit 版は MMX, SSE2, SSSE3 のどれかを表示する + printf("CPU extra\t:"); + if (cpu_flag & 1){ + if (cpu_flag & 256){ + printf(" SSSE3(old)"); + } else { + printf(" SSSE3"); + } + } else if (cpu_flag & 128){ + printf(" SSE2"); + } else { + printf(" MMX"); + } +#else // 64-bit 版は SSE2, SSSE3 を表示する + printf("CPU extra\t: x64"); + if (cpu_flag & 1){ + if (cpu_flag & 256){ + printf(" SSSE3(old)"); + } else { + printf(" SSSE3"); + } + } else if (cpu_flag & 128){ + printf(" SSE2"); + } +#endif + if (cpu_flag & 8) + printf(" CLMUL"); + if (cpu_flag & 16) + printf(" AVX2"); + printf("\nMemory usage\t: "); + if (memory_use & 7){ + printf("%d/8", memory_use & 7); + } else { + printf("Auto"); + } + statex.dwLength = sizeof(statex); + if (GlobalMemoryStatusEx(&statex)) + printf(" (%I64d MB available)", statex.ullAvailPhys >> 20); + if ((memory_use & ~7) == 0){ // HDD と SSD を自動判別する + check_seek_penalty(base_dir); + //printf("\n check_seek_penalty = %d\n", check_seek_penalty(base_dir)); + } + if (memory_use & 16){ // SSD + if (memory_use & 32){ + printf(", Fast SSD"); + } else { + printf(", SSD"); + } + } else if (memory_use & 8){ // HDD + printf(", HDD"); + } + printf("\n\n"); +} + +// 格納ファイル・リストを読み込んでバッファーに書き込む +static int read_list( + wchar_t *list_path, // リストのパス + unsigned int code_page) // CP_OEMCP か CP_UTF8 +{ + char buf[MAX_LEN * 3]; + wchar_t file_name[MAX_LEN], file_path[MAX_LEN]; + int len; + __int64 file_size; + FILE *fp; + WIN32_FILE_ATTRIBUTE_DATA AttrData; + + list_len = 0; + list_max = ALLOC_LEN; + list_buf = (wchar_t *)malloc(list_max * 2); + if (list_buf == NULL){ + printf("malloc, %d\n", list_max * 2); + return 1; + } + + // 読み込むファイルを開く + fp = _wfopen(list_path, L"rb"); + if (fp == NULL){ + printf_cp("cannot open file-list, %s\n", list_path); + return 1; + } + + // 一行ずつ読み込む + wcscpy(file_path, base_dir); + while (fgets(buf, MAX_LEN * 3, fp)){ + if (ferror(fp)) + break; + buf[MAX_LEN * 3 - 1] = 0; + // 末尾に改行があれば削除する + for (len = 0; len < MAX_LEN * 3; len++){ + if (buf[len] == 0) + break; + if ((buf[len] == '\n') || (buf[len] == '\r')){ + buf[len] = 0; + break; + } + } + if (buf[0] == 0) + continue; // 改行だけなら次の行へ + + // 読み込んだ内容をユニコードに変換する、末尾のディレクトリ記号の分を空けておく + if (!MultiByteToWideChar(code_page, 0, buf, -1, file_name, MAX_LEN - 1)){ + fclose(fp); + printf("MultiByteToWideChar, %s\n", buf); + return 1; + } + + // ファイルが基準ディレクトリ以下に存在することを確認する + len = copy_path_prefix(file_path, MAX_LEN - ADD_LEN, file_name, base_dir); // 絶対パスにしてから比較する + if (len == 0){ + fclose(fp); + printf_cp("filename is invalid, %s\n", file_name); + return 1; + } + if ((len <= base_len) || (_wcsnicmp(base_dir, file_path, base_len) != 0)){ // 基準ディレクトリ外なら + fclose(fp); + printf_cp("out of base-directory, %s\n", file_path); + return 1; + } + if (!GetFileAttributesEx(file_path, GetFileExInfoStandard, &AttrData)){ + print_win32_err(); + fclose(fp); + printf_cp("input file is not found, %s\n", file_path); + return 1; + } + + // ファイル名が重複しないようにする + wcscpy(file_name, file_path + base_len); + if (search_file_path(list_buf, list_len, file_name)) + continue; + + if (AttrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ // フォルダなら + len = (int)wcslen(file_name); + if (file_name[len - 1] != '\\'){ + file_name[len] = '\\'; // フォルダの末尾は必ず「\」にする + file_name[len + 1] = 0; + } + file_size = 0; + split_size = 0; + } else { // ファイルなら + file_size = ((__int64)AttrData.nFileSizeHigh << 32) | (__int64)AttrData.nFileSizeLow; + total_file_size += file_size; + if (file_size > 0) + entity_num++; + // サブ・ディレクトリまたはフォルダを含む場合はファイル分割を無効にする + if ((split_size != 0) && (wcschr(file_name, '\\') != NULL)) + split_size = 0; + } + + // リストにコピーする + if (add_file_path(file_name)){ + fclose(fp); + printf("add_file_path\n"); + return 1; + } + file_num++; + } + fclose(fp); +/* +fp = fopen("list_buf.txt", "wb"); +len = 0; +while (len < list_len){ + fwprintf(fp, L"%s\n", list_buf + len); + len += (wcslen(list_buf + len) + 1); +} +fclose(fp); +*/ + return 0; +} + +// ファイルを検索してファイル・リストに追加する +static int search_files( + wchar_t *search_path, // 検索するファイルのフル・パス、* ? も可 + int dir_len, // ディレクトリ部分の長さ + int file_only, // 0以外 = ファイルのみにする + int single_file) // -1 = *や?で検索指定、0~ = 単独指定 +{ + int len, dir_len2, old_num; + __int64 file_size; + HANDLE hFind; + WIN32_FIND_DATA FindData; + + if (list_buf == NULL){ + list_len = 0; + list_max = ALLOC_LEN; + list_buf = (wchar_t *)malloc(list_max * 2); + if (list_buf == NULL){ + printf("malloc, %d\n", list_max * 2); + return 1; + } + } + + // 末尾に「\」を付けてフォルダを指定してるなら内部を検索しない + len = (int)wcslen(search_path); + if (search_path[len - 1] == '\\'){ + unsigned int rv; + rv = GetFileAttributes(search_path); + if ((rv != INVALID_FILE_ATTRIBUTES) && (rv & FILE_ATTRIBUTE_DIRECTORY)){ // そのフォルダが存在するなら + if (!search_file_path(list_buf, list_len, search_path + base_len)){ // フォルダ名が重複しないようにする + // そのフォルダを追加する + if (add_file_path(search_path + base_len)){ + printf("add_file_path\n"); + return 1; + } + file_num++; + } + } + return 0; + } + + // 検索する + hFind = FindFirstFile(search_path, &FindData); + if (hFind == INVALID_HANDLE_VALUE) // 見つからなかったら + return 0; + do { + if ((wcscmp(FindData.cFileName, L".") == 0) || (wcscmp(FindData.cFileName, L"..") == 0)) + continue; // 自分や親のパスは無視する + if ((single_file < 0) && (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) + continue; // 検索中は隠し属性が付いてるファイルを無視する + + len = (int)wcslen(FindData.cFileName); // 見つけたファイル名の文字数 + if (dir_len + len >= MAX_LEN - ADD_LEN - 2){ // 末尾に「\*」を付けて再検索するので + FindClose(hFind); + printf("filename is too long\n"); + return 1; + } + // 現在のディレクトリ部分に見つかったファイル名を連結する + wcscpy(search_path + dir_len, FindData.cFileName); + + // フォルダなら + if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ + if (file_only == 0){ +/* + if (list2_buf){ // 途中のフォルダ名も比較して除外するなら + int off = 0; + while (off < list2_len){ + if (list2_buf[off++] == '-'){ // deny + if (PathMatchWild(search_path + base_len, list2_buf + off) != 0){ + printf_cp("deny : \"%s\"", search_path + base_len); + printf_cp(" : \"%s\"\n", list2_buf + off); + off = -1; + break; + } + } + off += wcslen(list2_buf + off) + 1; + } + if (off < 0) + continue; // 除外対象なら無視する + } +*/ + + // フォルダの末尾は「\」にする + wcscat(search_path, L"\\"); + if (!search_file_path(list_buf, list_len, search_path + base_len)){ // フォルダ名が重複しないようにする + split_size = 0; + old_num = file_num; // そのフォルダの中にファイルが何個あったのかを調べる + + // そのフォルダの中身を更に検索する + dir_len2 = (int)wcslen(search_path); + search_path[dir_len2 ] = '*'; // 末尾に「*」を追加する + search_path[dir_len2 + 1] = 0; + if (search_files(search_path, dir_len2, file_only, single_file)){ + FindClose(hFind); + printf("cannot search inner folder\n"); + return 1; + } + if (old_num == file_num){ // 空のフォルダも含める + search_path[dir_len2] = 0; // 検索用に追加した「*」を取り除く + + if (list2_buf){ // 除外するフォルダ名が指定されてるなら + if (exclude_path(search_path + base_len) != 0) + continue; // 除外対象なら無視する + } + + // リストにコピーする + if (add_file_path(search_path + base_len)){ + FindClose(hFind); + printf("add_file_path\n"); + return 1; + } + file_num++; + } + } + } + } else { // ファイルなら + if ((single_file < 0) && (list2_buf)){ // 除外するファイル名が指定されてるなら + if (exclude_path(search_path + base_len) != 0) + continue; // 除外対象なら無視する + } + + if (!search_file_path(list_buf, list_len, search_path + base_len)){ // ファイル名が重複しないようにする + // リストにコピーする + if (add_file_path(search_path + base_len)){ + FindClose(hFind); + printf("add_file_path\n"); + return 1; + } + file_num++; + file_size = ((__int64)FindData.nFileSizeHigh << 32) | (__int64)FindData.nFileSizeLow; + total_file_size += file_size; + if (file_size > 0) + entity_num++; + // サブ・ディレクトリまたはフォルダを含む場合はファイル分割を無効にする + if ((split_size != 0) && (wcschr(search_path + base_len, '\\') != NULL)) + split_size = 0; + } + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + + return 0; +} + +// 外部ファイルのリストを読み込んでバッファーに書き込む +static int read_external_list( + wchar_t *list_path, // リストのパス + unsigned int code_page) // CP_OEMCP か CP_UTF8 +{ + char buf[MAX_LEN * 3]; + wchar_t file_name[MAX_LEN], file_path[MAX_LEN], *tmp_p; + int len, rv; + FILE *fp; + + // 読み込むファイルを開く + fp = _wfopen(list_path, L"rb"); + if (fp == NULL){ + printf_cp("cannot open file-list, %s\n", list_path); + return 1; + } + + // 一行ずつ読み込む + wcscpy(file_path, base_dir); + while (fgets(buf, MAX_LEN * 3, fp)){ + if (ferror(fp)) + break; + buf[MAX_LEN * 3 - 1] = 0; + // 末尾に改行があれば削除する + for (len = 0; len < MAX_LEN * 3; len++){ + if (buf[len] == 0) + break; + if ((buf[len] == '\n') || (buf[len] == '\r')){ + buf[len] = 0; + break; + } + } + if (buf[0] == 0) + continue; // 改行だけなら次の行へ + + // 読み込んだ内容をユニコードに変換する、末尾のディレクトリ記号の分を空けておく + if (!MultiByteToWideChar(code_page, 0, buf, -1, file_name, MAX_LEN - 1)){ + fclose(fp); + printf("MultiByteToWideChar, %s\n", buf); + return 1; + } + + // ファイルが存在することを確認する + len = copy_path_prefix(file_path, MAX_LEN - ADD_LEN, file_name, NULL); + if (len == 0){ + fclose(fp); + printf_cp("filename is invalid, %s\n", file_name); + return 1; + } + //printf_cp("external file, %s\n", file_path); + rv = GetFileAttributes(file_path); + if ((rv == INVALID_FILE_ATTRIBUTES) || (rv & FILE_ATTRIBUTE_DIRECTORY)){ + fclose(fp); + printf_cp("external file is not found, %s\n", file_path); + return 1; + } + if (!search_file_path(list2_buf, list2_len, file_path)){ // ファイル名が重複しないようにする + if (list2_len + len >= list2_max){ // 領域が足りなくなるなら拡張する + list2_max += ALLOC_LEN; + tmp_p = (wchar_t *)realloc(list2_buf, list2_max * 2); + if (tmp_p == NULL){ + fclose(fp); + printf("realloc, %d\n", list2_max); + return 1; + } else { + list2_buf = tmp_p; + } + } + // そのファイルを追加する + //printf_cp("add external file, %s\n", file_path); + wcscpy(list2_buf + list2_len, file_path); + list2_len += len + 1; + } + } + fclose(fp); + + return 0; +} + +// 外部のファイルを検索してファイル・リストに追加する +static int search_external_files( + wchar_t *search_path, // 検索するファイルのフル・パス、* ? も可 + int dir_len, // ディレクトリ部分の長さ + int single_file) // -1 = *や?で検索指定、0~ = 単独指定 +{ + wchar_t *tmp_p; + int len; + HANDLE hFind; + WIN32_FIND_DATA FindData; + + // 検索する + hFind = FindFirstFile(search_path, &FindData); + if (hFind == INVALID_HANDLE_VALUE) // 見つからなかったら + return 0; + do { + if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + continue; // フォルダは無視する + if ((single_file < 0) && (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) + continue; // 検索中は隠し属性が付いてるファイルを無視する + len = dir_len + (int)wcslen(FindData.cFileName); + if (len >= MAX_LEN) + continue; // 長すぎるファイル名は無視する + + // 現在のディレクトリ部分に見つかったファイル名を連結する + wcscpy(search_path + dir_len, FindData.cFileName); + if (!search_file_path(list2_buf, list2_len, search_path)){ // ファイル名が重複しないようにする + if (list2_len + len >= list2_max){ // 領域が足りなくなるなら拡張する + list2_max += ALLOC_LEN; + tmp_p = (wchar_t *)realloc(list2_buf, list2_max * 2); + if (tmp_p == NULL){ + printf("realloc, %d\n", list2_max); + return 1; + } else { + list2_buf = tmp_p; + } + } + // そのファイルを追加する + //printf_cp("add external file, %s\n", search_path); + wcscpy(list2_buf + list2_len, search_path); + list2_len += len + 1; + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + + return 0; +} + +// リカバリ・ファイルとソース・ファイルが同じ名前にならないようにする +static int check_recovery_match(int switch_p) // インデックス・ファイルを作らない +{ + wchar_t recovery_base[MAX_LEN], file_ext[EXT_LEN], *tmp_p; + int num, len, list_off = 0, ext_len, recovery_len; + + // 基準ディレクトリ外にリカバリ・ファイルが存在するなら問題なし + if (_wcsnicmp(recovery_file, base_dir, base_len) != 0) + return 0; + + // リカバリ・ファイルの拡張子には指定されたものを使う + ext_len = 0; + wcscpy(recovery_base, recovery_file); + tmp_p = offset_file_name(recovery_base); + tmp_p = wcsrchr(tmp_p, '.'); + if (tmp_p != NULL){ + if (wcslen(tmp_p) < EXT_LEN){ + wcscpy(file_ext, tmp_p); // 拡張子を記録しておく + ext_len = (int)wcslen(file_ext); + *tmp_p = 0; // 拡張子を取り除く + } + } + recovery_len = (int)wcslen(recovery_base + base_len); + + // ファイルごとに比較する + for (num = 0; num < file_num; num++){ + tmp_p = list_buf + list_off; + while (list_buf[list_off] != 0) + list_off++; + list_off++; + + // ファイル名との前方一致を調べる + if (_wcsnicmp(recovery_base + base_len, tmp_p, recovery_len) == 0){ // リカバリ・ファイルと基準が同じ + len = (int)wcslen(tmp_p); + // 拡張子も同じか調べる + if ((ext_len != 0) && (len > ext_len)){ + if (_wcsnicmp(file_ext, tmp_p + (len - ext_len), ext_len) == 0){ + if (recovery_len + ext_len == len){ + // インデックス・ファイルと比較する + if (switch_p == 0){ + printf_cp("filename is invalid, %s\n", tmp_p); + return 1; + } + } else if (len - recovery_len - ext_len >= EXT_LEN){ + if (_wcsnicmp(tmp_p + recovery_len, L".vol", 4) == 0){ + printf_cp("filename is invalid, %s\n", tmp_p); + return 1; + } + } + } + } + } + } + return 0; +} + +#define MAX_ADJUST 1024 + +// 最適なブロック・サイズを調べる +static int check_block_size(unsigned int adjust_size, int alloc_method, int limit_num) +{ + wchar_t file_path[MAX_LEN]; + int list_off = 0, i, target, loop_count; + int lower_num, upper_num, new_num, sbc_rate; + int source_max_num, source_min_num, source_num_best; + unsigned int base_size, max_size, min_size, new_size, target_size; + unsigned int block_max_size, block_min_size, block_size_best; + __int64 *file_size, pad_size, pad_size_best; + WIN32_FILE_ATTRIBUTE_DATA AttrData; + + base_size = 4; + if (adjust_size != 0) + base_size = adjust_size; + + // 各ファイルのサイズを取得する + file_size = (__int64 *)malloc(sizeof(__int64) * file_num); + if (file_size == NULL){ + printf("malloc, %zd\n", sizeof(__int64) * file_num); + return 1; + } + wcscpy(file_path, base_dir); + block_max_size = 0; // ファイルが小さいとブロック・サイズの最大値も小さくなる + for (i = 0; i < file_num; i++){ + wcscpy(file_path + base_len, list_buf + list_off); + if (!GetFileAttributesEx(file_path, GetFileExInfoStandard, &AttrData)){ + print_win32_err(); + printf_cp("GetFileAttributesEx, %s\n", list_buf + list_off); + free(file_size); + return 1; + } + if (AttrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ // フォルダなら + file_size[i] = 0; + } else { + file_size[i] = ((__int64)AttrData.nFileSizeHigh << 32) | (unsigned __int64)AttrData.nFileSizeLow; + if (file_size[i] >= MAX_BLOCK_SIZE){ + block_max_size = MAX_BLOCK_SIZE; + } else if (file_size[i] > (__int64)block_max_size){ + block_max_size = (unsigned int)(file_size[i]); + } + } + while (list_buf[list_off] != 0) + list_off++; + list_off++; + } + + // ブロック・サイズの最大値 = ブロック数の最小値 + // 最大ファイルのサイズを単位の倍数にしたサイズのはず + if (base_size == 4){ // 切り上げで単位の倍数にする + block_max_size = (block_max_size + 3) & 0xFFFFFFFC; + } else { + block_max_size = ((block_max_size + base_size - 1) / base_size) * base_size; + if (block_max_size > MAX_BLOCK_SIZE) + block_max_size -= base_size; + } + source_min_num = 0; + for (i = 0; i < file_num; i++){ + if (file_size[i] > 0) + source_min_num += (int)((file_size[i] + (__int64)block_max_size - 1) / (__int64)block_max_size); + } + // ブロック・サイズの最小値 = ブロック数の最大値 + if ((source_min_num >= MAX_SOURCE_NUM) || (block_max_size <= base_size)){ // ファイルが多すぎ、または小さすぎ + source_max_num = source_min_num; + block_min_size = block_max_size; + } else { + pad_size = (total_file_size + MAX_SOURCE_NUM - 1) / MAX_SOURCE_NUM; // 最も多く分割するには + if (pad_size > (__int64)block_max_size - base_size) + pad_size = (__int64)block_max_size - base_size; + block_min_size = (unsigned int)pad_size; + if (base_size == 4){ // 切り上げで単位の倍数にする + block_min_size = (block_min_size + 3) & 0xFFFFFFFC; + } else { + block_min_size = ((block_min_size + base_size - 1) / base_size) * base_size; + } + source_max_num = 0; + for (i = 0; i < file_num; i++){ + if (file_size[i] > 0) + source_max_num += (int)((file_size[i] + (__int64)block_min_size - 1) / (__int64)block_min_size); + } + } + if (source_max_num > MAX_SOURCE_NUM){ // 最大ブロック数を超えたら、最も多くなるサイズを探す + i = source_max_num / source_min_num; // 超えた時と何倍ぐらい違うか + new_size = (block_min_size * i + block_max_size) / (1 + i); // 超えたサイズと最大ブロック・サイズの間 + if (base_size == 4){ // 切り捨てで単位の倍数にする + new_size &= 0xFFFFFFFC; + } else { + new_size -= new_size % base_size; + } + //printf("over = %d (%d blocks), rate = %d, new_size = %d\n", block_min_size, source_max_num, i, new_size); + min_size = block_min_size; + block_min_size = block_max_size; // 最小ブロック・サイズの最大値にする + source_max_num = source_min_num; // そのブロック数 + while (block_min_size - min_size > base_size){ + new_num = 0; + for (i = 0; i < file_num; i++){ + if (file_size[i] > 0) + new_num += (int)((file_size[i] + (__int64)new_size - 1) / (__int64)new_size); + } + if (new_num > MAX_SOURCE_NUM){ // 指定したブロック数を越えたら、ブロック・サイズを大きくする + min_size = new_size; // 越えたサイズを最小値にする + } else { // ブロック・サイズを小さくする + source_max_num = new_num; + block_min_size = new_size; // 越えなかったら最小ブロック・サイズの最大値にする + } + new_size = (min_size + block_min_size) / 2; // 最小値と最大値の中間 + if (base_size == 4){ // 切り捨てで単位の倍数にする + new_size &= 0xFFFFFFFC; + } else { + new_size -= new_size % base_size; + } + if (new_size <= min_size) + new_size = min_size + base_size; + //printf("over size = %d, ok = %d (%d blocks)\n", min_size, block_min_size, source_max_num); + } + } + //printf("max = %d (%d blocks), min = %d (%d blocks)\n", block_max_size, source_min_num, block_min_size, source_max_num); + + // ブロック・サイズの初期値を決める + max_size = block_max_size; + min_size = block_min_size; + lower_num = source_min_num; + upper_num = source_max_num; + if (alloc_method > 0){ // ブロック・サイズが指定された + target_size = alloc_method; + if (target_size >= block_max_size){ // ブロック・サイズの範囲内にする + target_size = block_max_size; + block_size = target_size; + } else if (target_size <= block_min_size){ + target_size = block_min_size; + block_size = target_size; + } else { // 単位の倍数で指定されたサイズに近い方にする + block_size = ((target_size + (base_size / 2)) / base_size) * base_size; + if (block_size > block_max_size) + block_size -= base_size; + if (block_size < block_min_size) + block_size += base_size; + } + source_num = 0; + for (i = 0; i < file_num; i++){ + if (file_size[i] > 0) // ブロック数を計算する + source_num += (int)((file_size[i] + block_size - 1) / (__int64)block_size); + } + if ((limit_num > 0) && (source_num > limit_num)){ // ブロック数の制限を越えたら + alloc_method = 0; // ブロック数を指定しなおす + max_size = block_max_size; + min_size = block_min_size; + lower_num = source_min_num; + upper_num = source_max_num; + } + + } else if (alloc_method < 0){ // 割合(ブロック・サイズ * ? = ブロック数)が指定された + target = -alloc_method; // 識別用に負にしてたのを正に戻す + // ブロック・サイズの初期値は単純に合計ファイル・サイズから計算する + // SBC=root(F*rate/10000)=root(F*rate)/100 + pad_size = total_file_size * (__int64)target; // rate/10000 だが root後なら /100 になる + new_num = sqrt64(pad_size); + new_num = (new_num + 50) / 100; // 小数点以下は四捨五入する + if (new_num > source_max_num){ + new_num = source_max_num; + } else if (new_num < source_min_num){ + new_num = source_min_num; + } + if (new_num < 2) + new_num = 2; // 最低でも2ブロックにする + pad_size = new_num; + pad_size = (total_file_size + pad_size - 1) / pad_size; + if (pad_size >= (__int64)block_max_size){ + new_size = block_max_size; + } else { + new_size = (unsigned int)pad_size; + if (new_size <= block_min_size){ + new_size = block_min_size; + } else { + new_size = ((new_size + (base_size / 2)) / base_size) * base_size; // 四捨五入して単位の倍数にする + } + } + // 目標の割合を範囲内にする + sbc_rate = ((10000 * source_min_num) + (block_max_size / 2)) / block_max_size; + if (target <= sbc_rate){ + if (sbc_rate < 10){ + target = 10; // 0.1% 未満にはしない + } else { + target = sbc_rate; + } + } else { + sbc_rate = ((10000 * source_max_num) + (block_min_size / 2)) / block_min_size; + if (target > sbc_rate) + target = sbc_rate; + } + //printf("target rate = %d.%02d%%, size = %d, max = %d, min = %d\n", target /100, target %100, new_size, max_size, min_size); + // 目標に近づける + while (max_size - min_size > base_size){ + new_num = 0; + for (i = 0; i < file_num; i++){ + if (file_size[i] > 0) + new_num += (int)((file_size[i] + new_size - 1) / (__int64)new_size); + } + + sbc_rate = ((10000 * new_num) + (new_size / 2)) / new_size; + //printf(" new_num = %d, new_size = %d, rate = %d.%02d%%\n", new_num, new_size, sbc_rate /100, sbc_rate %100); + if (sbc_rate > target){ // 指定した割合を越えたら、ブロック・サイズを大きくする + upper_num = new_num; + min_size = new_size; // 越えたサイズを最小値にする + new_size = (min_size + max_size) / 2; // 最小値と最大値の中間 + if (base_size == 4){ // 切り上げで単位の倍数にする + new_size = (new_size + 3) & 0xFFFFFFFC; + } else { + new_size = ((new_size + base_size - 1) / base_size) * base_size; + } + if (new_size >= max_size) + new_size = max_size - base_size; + } else { // ブロック・サイズを小さくする + lower_num = new_num; + max_size = new_size; // 越えなかったサイズを最大値にする + new_size = (min_size + max_size) / 2; // 最小値と最大値の中間 + if (base_size == 4){ // 切り捨てで単位の倍数にする + new_size &= 0xFFFFFFFC; + } else { + new_size -= new_size % base_size; + } + if (new_size <= min_size) + new_size = min_size + base_size; + } + //printf(" lower = %d, max = %d, upper = %d, min = %d\n", lower_num, max_size, upper_num, min_size); + if (lower_num == upper_num) + break; + } + if (lower_num == upper_num){ // 同じブロック数なら小さいブロック・サイズの方が効率がいい + block_size = min_size; + source_num = upper_num; + } else { + sbc_rate = ((10000 * lower_num) + (max_size / 2)) / max_size; + i = ((10000 * upper_num) + (min_size / 2)) / min_size; + // 割合が近い方にする + if ((target - sbc_rate) < (i - target)){ + block_size = max_size; + source_num = lower_num; + } else { + block_size = min_size; + source_num = upper_num; + } + } + if ((limit_num > 0) && (source_num > limit_num)){ // ブロック数の制限を越えたら + if (lower_num > limit_num){ + alloc_method = 0; // ブロック数を指定しなおす + max_size = block_max_size; + min_size = block_min_size; + lower_num = source_min_num; + upper_num = source_max_num; + } else { // 少ない方を使う + block_size = max_size; + source_num = lower_num; + } + } + } + if (alloc_method == 0){ // ブロック数が指定された、またはブロック数の制限を越えた + target = limit_num; + if (target > source_max_num){ // 目標ブロック数を範囲内にする + target = source_max_num; + } else if (target < source_min_num){ + target = source_min_num; + } + // ブロック・サイズの初期値は単純に合計ファイル・サイズを目標ブロック数で割る + pad_size = target; + pad_size = (total_file_size + pad_size - 1) / pad_size; + if (pad_size >= (__int64)block_max_size){ + new_size = block_max_size; + } else { + new_size = (unsigned int)pad_size; + if (new_size <= block_min_size){ + new_size = block_min_size; + } else { + new_size = ((new_size + (base_size / 2)) / base_size) * base_size; // 四捨五入して単位の倍数にする + } + } + //printf("target num = %d, size = %d, max = %d, min = %d\n", target, new_size, max_size, min_size); + // 目標に近づける + while (max_size - min_size > base_size){ + new_num = 0; + for (i = 0; i < file_num; i++){ + if (file_size[i] > 0) + new_num += (int)((file_size[i] + new_size - 1) / (__int64)new_size); + } + //printf(" new_num = %d, new_size = %d\n", new_num, new_size); + + if (new_num > target){ // 指定したブロック数を越えたら、ブロック・サイズを大きくする + upper_num = new_num; + min_size = new_size; // 越えたサイズを最小値にする + new_size = (min_size + max_size) / 2; // 最小値と最大値の中間 + if (base_size == 4){ // 切り上げで単位の倍数にする + new_size = (new_size + 3) & 0xFFFFFFFC; + } else { + new_size = ((new_size + base_size - 1) / base_size) * base_size; + } + if (new_size >= max_size) + new_size = max_size - base_size; + } else { // ブロック・サイズを小さくする + lower_num = new_num; + max_size = new_size; // 越えなかったサイズを最大値にする + new_size = (min_size + max_size) / 2; // 最小値と最大値の中間 + if (base_size == 4){ // 切り捨てで単位の倍数にする + new_size &= 0xFFFFFFFC; + } else { + new_size -= new_size % base_size; + } + if (new_size <= min_size) + new_size = min_size + base_size; + } + //printf(" lower = %d, max = %d, upper = %d, min = %d\n", lower_num, max_size, upper_num, min_size); + } + // ブロック数が近い方にする + if ((target - lower_num) < (upper_num - target)){ + block_size = max_size; + source_num = lower_num; + } else { + block_size = min_size; + source_num = upper_num; + } + } + + // とりあえず最適値に設定しておく + block_size_best = block_size; + source_num_best = source_num; + pad_size = ((__int64)block_size * (__int64)source_num) - total_file_size; + pad_size_best = pad_size; +/* sbc_rate = ((10000 * source_num) + (block_size / 2)) / block_size; + i = (int)((1000 * total_file_size) / (total_file_size + pad_size)); + printf("start: size= %d, count= %d, sbc= %d.%02d%%, pad= %I64d, rate= %d.%d%%\n", + block_size, source_num, sbc_rate /100, sbc_rate %100, pad_size, i /10, i %10); +*/ + if (pad_size > 0){ // 近辺で効率が良くなる所を探す + if (alloc_method == 0){ // ブロック数が指定された + // ブロック・サイズを大きくしてみる + loop_count = 0; + new_size = block_size + base_size; + while ((loop_count < MAX_ADJUST * 2) && (new_size <= block_max_size)){ + loop_count++; + new_num = 0; + for (i = 0; i < file_num; i++){ + if (file_size[i] > 0) // ブロック数を計算する + new_num += (int)((file_size[i] + new_size - 1) / (__int64)new_size); + } + if (new_num < target - (target / 8)) + break; // 目標から離れすぎるとだめ -12% + pad_size = ((__int64)new_size * (__int64)new_num) - total_file_size; + if (pad_size < pad_size_best){ + block_size_best = new_size; + source_num_best = new_num; + pad_size_best = pad_size; + if (pad_size == 0) + break; + } + //i = (int)((1000 * total_file_size) / (total_file_size + pad_size)); + //printf("+ : size = %d, count = %d, pad = %I64d, rate = %d.%d%%\n", + // new_size, new_num, pad_size, i / 10, i % 10); + new_size += base_size; + } + //printf("+ : %d trials\n", loop_count); + // ブロック・サイズを小さくしてみる + loop_count = 0; + new_size = block_size - base_size; + while ((loop_count < MAX_ADJUST) && (new_size >= block_min_size)){ + loop_count++; + new_num = 0; + for (i = 0; i < file_num; i++){ + if (file_size[i] > 0) // ブロック数を計算する + new_num += (int)((file_size[i] + new_size - 1) / (__int64)new_size); + } + if (new_num > target + (target / 16)) + break; // 目標から離れすぎるとだめ +6% + pad_size = ((__int64)new_size * (__int64)new_num) - total_file_size; + if (pad_size < pad_size_best){ + block_size_best = new_size; + source_num_best = new_num; + pad_size_best = pad_size; + if (pad_size == 0) + break; + } + //i = (int)((1000 * total_file_size) / (total_file_size + pad_size)); + //printf("- : size = %d, count = %d, pad = %I64d, rate = %d.%d%%\n", + // new_size, new_num, pad_size, i / 10, i % 10); + new_size -= base_size; + } + //printf("- : %d trials\n", loop_count); + + } else if (alloc_method < 0){ // 割合(ブロック・サイズ * ? = ブロック数)が指定された + // ブロック・サイズを大きくしてみる + loop_count = 0; + new_size = block_size + base_size; + while ((loop_count < MAX_ADJUST * 2) && (new_size <= block_max_size)){ + loop_count++; + new_num = 0; + for (i = 0; i < file_num; i++){ + if (file_size[i] > 0) // ブロック数を計算する + new_num += (int)((file_size[i] + new_size - 1) / (__int64)new_size); + } + sbc_rate = ((10000 * new_num) + (new_size / 2)) / new_size; // 四捨五入する + if (sbc_rate < target - 9) + break; // 目標から離れすぎるとだめ -0.09% + pad_size = ((__int64)new_size * (__int64)new_num) - total_file_size; + if (pad_size < pad_size_best){ + block_size_best = new_size; + source_num_best = new_num; + pad_size_best = pad_size; + if (pad_size == 0) + break; + } + //i = (int)((1000 * total_file_size) / (total_file_size + pad_size)); + //printf("+ : size= %d, count= %d, sbc= %d.%02d%%, pad= %I64d, rate= %d.%d%%\n", + // new_size, source_num, sbc_rate /100, sbc_rate %100, pad_size, i /10, i %10); + new_size += base_size; + } + //printf("+ : %d trials\n", loop_count); + // ブロック・サイズを小さくしてみる + loop_count = 0; + new_size = block_size - base_size; + while ((loop_count < MAX_ADJUST) && (new_size >= block_min_size)){ + loop_count++; + new_num = 0; + for (i = 0; i < file_num; i++){ + if (file_size[i] > 0) // ブロック数を計算する + new_num += (int)((file_size[i] + new_size - 1) / (__int64)new_size); + } + if ((limit_num > 0) && (new_num > limit_num + (limit_num / 16))) + break; // 制限ブロック数を越えるとだめ +6% + sbc_rate = ((10000 * new_num) + (new_size / 2)) / new_size; // 四捨五入する + if (sbc_rate > target + 9) + break; // 目標から離れすぎるとだめ +0.09% + pad_size = ((__int64)new_size * (__int64)new_num) - total_file_size; + if (pad_size < pad_size_best){ + block_size_best = new_size; + source_num_best = new_num; + pad_size_best = pad_size; + if (pad_size == 0) + break; + } + //i = (int)((1000 * total_file_size) / (total_file_size + pad_size)); + //printf("- : size= %d, count= %d, sbc= %d.%02d%%, pad= %I64d, rate= %d.%d%%\n", + // new_size, new_num, sbc_rate /100, sbc_rate %100, pad_size, i /10, i %10); + new_size -= base_size; + } + //printf("- : %d trials\n", loop_count); + + } else if (adjust_size > 0){ // ブロック・サイズと変更単位の両方が指定された時だけ、自動調整する + // ブロック・サイズを大きくしてみる + loop_count = 0; + new_size = block_size + base_size; + while ((loop_count < MAX_ADJUST * 2) && (new_size <= block_max_size)){ + loop_count++; + if (new_size > target_size + (target_size / 2)) + break; // 目標から離れすぎるとだめ +50% + new_num = 0; + for (i = 0; i < file_num; i++){ + if (file_size[i] > 0) // ブロック数を計算する + new_num += (int)((file_size[i] + new_size - 1) / (__int64)new_size); + } + pad_size = ((__int64)new_size * (__int64)new_num) - total_file_size; + if (pad_size < pad_size_best){ + block_size_best = new_size; + source_num_best = new_num; + pad_size_best = pad_size; + if (pad_size == 0) + break; + } + //i = (int)((1000 * total_file_size) / (total_file_size + pad_size)); + //printf("+ : size = %d, count = %d, pad = %I64d, rate = %d.%d%%\n", + // new_size, new_num, pad_size, i / 10, i % 10); + new_size += base_size; + } + //printf("+ : %d trials\n", loop_count); + // ブロック・サイズを小さくしてみる + loop_count = 0; + new_size = block_size - base_size; + while ((loop_count < MAX_ADJUST) && (new_size >= block_min_size)){ + loop_count++; + if (new_size < target_size - (target_size / 4)) + break; // 目標から離れすぎるとだめ -25% + new_num = 0; + for (i = 0; i < file_num; i++){ + if (file_size[i] > 0) // ブロック数を計算する + new_num += (int)((file_size[i] + new_size - 1) / (__int64)new_size); + } + if ((limit_num > 0) && (new_num > limit_num + (limit_num / 16))) + break; // 制限ブロック数を越えるとだめ +6% + pad_size = ((__int64)new_size * (__int64)new_num) - total_file_size; + if (pad_size < pad_size_best){ + block_size_best = new_size; + source_num_best = new_num; + pad_size_best = pad_size; + if (pad_size == 0) + break; + } + //i = (int)((1000 * total_file_size) / (total_file_size + pad_size)); + //printf("- : size = %d, count = %d, pad = %I64d, rate = %d.%d%%\n", + // new_size, new_num, pad_size, i / 10, i % 10); + new_size -= base_size; + } + //printf("- : %d trials\n", loop_count); + } + } + + // 最適値を使う +/* sbc_rate = ((10000 * source_num_best) + (block_size_best / 2)) / block_size_best; + i = (int)((1000 * total_file_size) / (total_file_size + pad_size_best)); + printf("best : size= %d, count= %d, sbc= %d.%02d%%, pad= %I64d, rate= %d.%d%%\n\n", + block_size_best, source_num_best, sbc_rate /100, sbc_rate %100, pad_size_best, i /10, i %10); +*/ + block_size = block_size_best; + source_num = source_num_best; + if (recovery_limit < 0){ // ソース・ファイルの最大スライス数を計算する + recovery_limit = -1; // 初期化する + for (i = 0; i < file_num; i++){ + if (file_size[i] > 0){ // スライス数を計算する + upper_num = (int)((file_size[i] + block_size - 1) / (__int64)block_size); + if (upper_num + recovery_limit > 0) + recovery_limit = -upper_num; + } + } + } + + free(file_size); + return 0; +} + +// 復元したいファイル数から、必要なパリティ・ブロック数を計算する +static int calc_required_parity(int possible_count) +{ + wchar_t file_path[MAX_LEN]; + int list_off = 0, i, block_need, extra_rate, id = 0, block_count = 0; + int *file_block; + __int64 file_size; + WIN32_FILE_ATTRIBUTE_DATA AttrData; + + if (possible_count <= 0) + return 0; + extra_rate = possible_count % 100; + possible_count /= 100; + //printf("possible_count = %d, extra_rate = %d\n", possible_count, extra_rate); + if (possible_count >= entity_num) + return source_num; + + // 各ファイルのソース・ブロック数を取得する + file_block = (int *)malloc(sizeof(int) * file_num); + if (file_block == NULL){ + printf("malloc, %zd\n", sizeof(int) * file_num); + return -1; + } + wcscpy(file_path, base_dir); + for (i = 0; i < file_num; i++){ + wcscpy(file_path + base_len, list_buf + list_off); + if (!GetFileAttributesEx(file_path, GetFileExInfoStandard, &AttrData)){ + print_win32_err(); + printf_cp("GetFileAttributesEx, %s\n", list_buf + list_off); + free(file_block); + return -1; + } + if (AttrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ // フォルダなら + file_block[i] = 0; + } else { + file_size = ((__int64)AttrData.nFileSizeHigh << 32) | (unsigned __int64)AttrData.nFileSizeLow; + file_block[i] = (int)((file_size + block_size) / block_size); + if (block_count < file_block[i]){ + block_count = file_block[i]; + id = i; + } + } + //printf("%d: %d blocks\n", i, file_block[i]); + while (list_buf[list_off] != 0) + list_off++; + list_off++; + } + //printf("max block = %d, file = %d\n", block_count, id); + block_need = block_count; + + if (possible_count < 1){ + block_need = (block_need * extra_rate + 99) / 100; + } else { + while (possible_count > 1){ + // ブロック数が最大だったファイルを取り除く + file_block[id] = 0; + block_count = 0; + + // その次に大きなファイルを探す + for (i = 0; i < file_num; i++){ + if (block_count < file_block[i]){ + block_count = file_block[i]; + id = i; + } + } + if (block_count == 0) + break; + block_need += block_count; + //printf("max block = %d, file = %d, sum = %d\n", block_count, id, block_need); + possible_count--; + } + block_need = (block_need * (100 + extra_rate) + 99) / 100; + if (block_need > source_num) + block_need = source_num; + } + + free(file_block); + return block_need; +} + +wmain(int argc, wchar_t *argv[]) +{ + wchar_t uni_buf[MAX_LEN], *tmp_p; + int i, j, k; + unsigned int switch_set = 0; +/* +in= switch_set & 0x00000001 +f = switch_set & 0x00000002 +fu= switch_set & 0x00000004 +up= switch_set & 0x00000008 +h = switch_set & 0x00000010 +w = switch_set & 0x00000020 +lp= switch_set & 0x00000700 +rd= switch_set & 0x00030000 +ri= switch_set & 0x00040000 +*/ + printf("Parchive 2.0 client version " FILE_VERSION " by Yutaka Sawada\n\n"); + if (argc < 3){ + printf("Self-Test: "); + i = par2_checksum(uni_buf); + if (i == 0){ + printf("Success"); + } else if (i == 2){ + printf("PE checksum is different"); + } else if (i == 3){ + printf("CRC-32 is different"); + } else { + printf("Error\0thedummytext"); + } + printf("\n\n"); + print_help(); + return 0; + } + + // 初期化 + recovery_file[0] = 0; + base_dir[0] = 0; + ini_path[0] = 0; + uni_buf[0] = 0; + file_num = 0; + entity_num = 0; + recovery_num = 0; + source_num = 0; + parity_num = 0; + recovery_limit = 0; + first_num = 0; + switch_v = 0; + switch_b = 0; + block_size = 0; + split_size = 0; + total_file_size = 0; + list_buf = NULL; + recv_buf = NULL; + recv2_buf = NULL; + list2_buf = NULL; + cp_output = GetConsoleOutputCP(); + check_cpu(); // CPU を検査する + + // コマンド + switch (argv[1][0]){ + case 'c': // create + case 't': // trial + case 'v': // verify + case 'r': // repair + case 'l': // list + break; + default: + print_help(); + return 0; + } + + // オプションとリカバリ・ファイルの指定 + for (i = 2; i < argc; i++){ + tmp_p = argv[i]; + // オプション + if (((tmp_p[0] == '/') || (tmp_p[0] == '-')) && (tmp_p[0] == argv[2][0])){ + tmp_p++; // 先頭の識別文字をとばす + // 作成時のオプション + if (wcscmp(tmp_p, L"ri") == 0){ + switch_set |= 0x40000; + } else if (wcscmp(tmp_p, L"in") == 0){ + switch_set |= 0x01; + } else if (wcscmp(tmp_p, L"up") == 0){ + switch_set |= 0x08; + // 検査時のオプション + } else if (wcscmp(tmp_p, L"h") == 0){ + switch_set |= 0x10; + } else if (wcscmp(tmp_p, L"p") == 0){ + if ((argv[1][0] == 'v') || (argv[1][0] == 'r')) + switch_b |= 0x10; + } else if (wcscmp(tmp_p, L"w") == 0){ + switch_set |= 0x20; + // 修復時のオプション + } else if (wcscmp(tmp_p, L"b") == 0){ + if (argv[1][0] == 'r') + switch_b |= 1; + } else if (wcscmp(tmp_p, L"br") == 0){ + if (argv[1][0] == 'r') + switch_b |= 2; + } else if (wcscmp(tmp_p, L"bi") == 0){ + if (argv[1][0] == 'r') + switch_b |= 4; + // 共通のオプション + } else if (wcscmp(tmp_p, L"f") == 0){ + switch_set |= 0x02; + } else if (wcscmp(tmp_p, L"fu") == 0){ + switch_set |= 0x06; + } else if (wcscmp(tmp_p, L"fo") == 0){ + switch_v |= 8; + } else if (wcscmp(tmp_p, L"uo") == 0){ + cp_output = CP_UTF8; + + // 作成時のオプション (数値) + } else if (wcsncmp(tmp_p, L"ss", 2) == 0){ + if ((argv[1][0] == 'c') || (argv[1][0] == 't')){ + block_size = 0; + j = 2; + while ((j < 2 + 10) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + block_size = (block_size * 10) + (tmp_p[j] - '0'); + j++; + } + // この時点ではまだ単位がわからないので近似しない + if (block_size > MAX_BLOCK_SIZE) + block_size = MAX_BLOCK_SIZE; + switch_b &= 0x003FFFFF; // 割合の指定を取り消す + } + } else if (wcsncmp(tmp_p, L"sn", 2) == 0){ + source_num = 0; + j = 2; + while ((j < 2 + 6) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + source_num = (source_num * 10) + (tmp_p[j] - '0'); + j++; + } + if (source_num > MAX_SOURCE_NUM) + source_num = MAX_SOURCE_NUM; + } else if (wcsncmp(tmp_p, L"sr", 2) == 0){ + if ((argv[1][0] == 'c') || (argv[1][0] == 't')){ + k = 0; + j = 2; + while ((j < 2 + 5) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + k = (k * 10) + (tmp_p[j] - '0'); + j++; + } + if (k > 1023) + k = 1023; + switch_b = (switch_b & 0x003FFFFF) | (k << 22); + block_size = 0; // ブロック・サイズの指定を取り消す + } + } else if (wcsncmp(tmp_p, L"sm", 2) == 0){ + if ((argv[1][0] == 'c') || (argv[1][0] == 't')){ + k = 0; + j = 2; + while ((j < 2 + 8) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + k = (k * 10) + (tmp_p[j] - '0'); + j++; + } + if (k > 4194300) + k = 4194300; + k &= 0x003FFFFC; // 4の倍数にする + switch_b = (switch_b & 0xFFC00000) | k; + } + } else if (wcsncmp(tmp_p, L"lp", 2) == 0){ + j = 4; // lp は lp4 と同じにする + if ((tmp_p[2] >= '0') && (tmp_p[2] <= '7')) + j = tmp_p[2] - '0'; + if ((switch_set & 0x0700) == 0) + switch_set |= (j << 8); + } else if (wcsncmp(tmp_p, L"rn", 2) == 0){ + parity_num = 0; + j = 2; + while ((j < 2 + 6) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + parity_num = (parity_num * 10) + (tmp_p[j] - '0'); + j++; + } + if (parity_num > MAX_PARITY_NUM) + parity_num = MAX_PARITY_NUM; + } else if (wcsncmp(tmp_p, L"rr", 2) == 0){ + parity_num = 0; + j = 2; + while ((j < 2 + 5) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + parity_num = (parity_num * 10) + (tmp_p[j] - '0'); + j++; + } + parity_num *= 100; + if ((tmp_p[j] == '.') || (tmp_p[j] == ',')){ // 小数点があるなら、小数点以下第二位まで読み取る + j++; + if ((tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + parity_num += (tmp_p[j] - '0') * 10; + j++; + if ((tmp_p[j] >= '0') && (tmp_p[j] <= '9')) + parity_num += tmp_p[j] - '0'; + } + } + if (parity_num > 100000) + parity_num = 100000; + parity_num *= -1; + } else if (wcsncmp(tmp_p, L"rp", 2) == 0){ + parity_num = 0; + j = 2; + while ((j < 2 + 4) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + parity_num = (parity_num * 10) + (tmp_p[j] - '0'); + j++; + } + if (parity_num > 999) + parity_num = 999; + parity_num *= 100; + if ((tmp_p[j] == '.') || (tmp_p[j] == ',')){ // 小数点があるなら、小数点以下第二位まで読み取る + j++; + if ((tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + parity_num += (tmp_p[j] - '0') * 10; + j++; + if ((tmp_p[j] >= '0') && (tmp_p[j] <= '9')) + parity_num += tmp_p[j] - '0'; + } + } + parity_num = parity_num * -1 - 200000; + } else if (wcsncmp(tmp_p, L"rd", 2) == 0){ + j = 0; // rd は rd0 と同じにする + if ((tmp_p[2] >= '0') && (tmp_p[2] <= '3')) // 0~3 の範囲 + j = tmp_p[2] - '0'; + if ((switch_set & 0x30000) == 0) + switch_set |= (j << 16); + } else if (wcsncmp(tmp_p, L"rf", 2) == 0){ + recovery_num = 0; + j = 2; + while ((j < 2 + 6) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + recovery_num = (recovery_num * 10) + (tmp_p[j] - '0'); + j++; + } + if (recovery_num > MAX_PARITY_NUM) + recovery_num = MAX_PARITY_NUM; + } else if (wcsncmp(tmp_p, L"rs", 2) == 0){ + first_num = 0; + j = 2; + while ((j < 2 + 6) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + first_num = (first_num * 10) + (tmp_p[j] - '0'); + j++; + } + if (first_num > MAX_PARITY_NUM - 1) + first_num = MAX_PARITY_NUM - 1; + } else if (wcsncmp(tmp_p, L"lr", 2) == 0){ + recovery_limit = 0; + j = 2; + while ((j < 2 + 6) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + recovery_limit = (recovery_limit * 10) + (tmp_p[j] - '0'); + j++; + } + if (recovery_limit <= 0) // -lr0 ならソース・ファイルの最大ブロック数と同じにする + recovery_limit = -1; + if (recovery_limit > MAX_PARITY_NUM) + recovery_limit = MAX_PARITY_NUM; + } else if (wcsncmp(tmp_p, L"ls", 2) == 0){ + __int64 num8 = 0; + j = 2; + while ((j < 2 + 10) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + num8 = (num8 * 10) + (tmp_p[j] - '0'); + j++; + } + if (num8 >= 0xFFFFFFFC){ + split_size = 0xFFFFFFFC; // 分割サイズは 4GBまで + } else { + split_size = (unsigned int)num8; + } + // 検査時のオプション (数値) + } else if (wcsncmp(tmp_p, L"vl", 2) == 0){ + j = 0; // vl は vl0 と同じにする + if ((tmp_p[2] >= '0') && (tmp_p[2] <= '7')) // 0~7 の範囲 + j = tmp_p[2] - '0'; + if ((switch_v & 7) == 0) + switch_v |= j; + // 共通のオプション (数値) + } else if (wcsncmp(tmp_p, L"lc", 2) == 0){ + k = 0; + j = 2; + while ((j < 2 + 5) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + k = (k * 10) + (tmp_p[j] - '0'); + j++; + } + if (k & 32){ // GPU を使う + OpenCL_method = 1; // Faster GPU + } else if (k & 64){ + OpenCL_method = -1; // Slower GPU + } + if (k & 16) // SSSE3 を使わない + cpu_flag &= 0xFFFFFFFE; + if (k & 128) // CLMUL を使わない、SSSE3 の古いエンコーダーを使う + cpu_flag = (cpu_flag & 0xFFFFFFF7) | 0x100; + if (k & 256) // JIT(SSE2) を使わない + cpu_flag &= 0xFFFFFF7F; + if (k & 512) // AVX2 を使わない + cpu_flag &= 0xFFFFFFEF; + if (k & 15){ // 使用するコア数を変更する + k &= 15; // 1~15 の範囲 + // printf("\n lc# = %d , logical = %d, physical = %d \n", k, cpu_num >> 24, (cpu_num & 0x00FF0000) >> 16); + if (k == 12){ // 物理コア数の 1/4 にする + k = ((cpu_num & 0x00FF0000) >> 16) / 4; + } else if (k == 13){ // 物理コア数の半分にする + k = ((cpu_num & 0x00FF0000) >> 16) / 2; + } else if (k == 14){ // 物理コア数の 3/4 にする + k = (((cpu_num & 0x00FF0000) >> 16) * 3) / 4; + } else if (k == 15){ // 物理コア数にする + k = (cpu_num & 0x00FF0000) >> 16; + if (k >= 6) + k--; // 物理コア数が 6以上なら、1個減らす + } else if (k > (cpu_num >> 24)){ + k = cpu_num >> 24; // 論理コア数を超えないようにする + } + if (k > MAX_CPU){ + k = MAX_CPU; + } else if (k < 1){ + k = 1; + } + cpu_num = (cpu_num & 0xFFFF0000) | k; // 指定されたコア数を下位に配置する + } + } else if (wcsncmp(tmp_p, L"m", 1) == 0){ + memory_use = 0; + j = 1; // メモリー使用量だけでなく、モード切替用としても使う、2桁まで + while ((j < 1 + 2) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + memory_use = (memory_use * 10) + (tmp_p[j] - '0'); + j++; + } + } else if (wcsncmp(tmp_p, L"vs", 2) == 0){ + recent_data = 0; + j = 2; + while ((j < 2 + 2) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + recent_data = (recent_data * 10) + (tmp_p[j] - '0'); + j++; + } + if ((recent_data == 8) || (recent_data > 15)) + recent_data = 0; + + // 作成時のオプション (文字列) + } else if (wcsncmp(tmp_p, L"c", 1) == 0){ + tmp_p++; + if (wcslen(tmp_p) >= COMMENT_LEN){ + printf("comment is too long\n"); + return 1; + } + wcscpy(uni_buf, tmp_p); + } else if ((wcsncmp(tmp_p, L"fa", 2) == 0) || (wcsncmp(tmp_p, L"fe", 2) == 0)){ + tmp_p += 2; + if ((argv[1][0] == 'c') || (argv[1][0] == 't')){ + j = (int)wcslen(tmp_p); + if ((j == 0) || (list2_len + j + 1 > ALLOC_LEN - MAX_LEN)) + continue; + if (list2_buf == NULL){ + list2_max = 0; // allow list の項目数 + list2_len = 0; + list2_buf = (wchar_t *)malloc(ALLOC_LEN * 2); + } + if (list2_buf != NULL){ + if (tmp_p[-1] == 'a'){ + list2_max++; + list2_buf[list2_len++] = '+'; + } else { + list2_buf[list2_len++] = '-'; + } + j = copy_wild(list2_buf + list2_len, tmp_p); + list2_len += j; + } + } + // 共通のオプション (文字列) + } else if (wcsncmp(tmp_p, L"vd", 2) == 0){ + tmp_p += 2; + j = copy_path_prefix(ini_path, MAX_LEN - INI_NAME_LEN - 1, tmp_p, NULL); + if (j == 0){ + printf("save-directory is invalid\n"); + return 1; + } + if (ini_path[j - 1] != '\\'){ // 末尾が「\」でなければ付けておく + ini_path[j ] = '\\'; + ini_path[j + 1] = 0; + } + //printf("Save Directory : %S\n", ini_path); + j = GetFileAttributes(ini_path); + if ((j == INVALID_FILE_ATTRIBUTES) || !(j & FILE_ATTRIBUTE_DIRECTORY)){ + printf("save-directory is invalid\n"); + return 1; + } + } else if (wcsncmp(tmp_p, L"d", 1) == 0){ + tmp_p++; + j = copy_path_prefix(base_dir, MAX_LEN - 2, tmp_p, NULL); // 末尾に追加される分の余裕を見ておく + if (j == 0){ + printf("base-directory is invalid\n"); + return 1; + } + if (base_dir[j - 1] != '\\'){ // 末尾が「\」でなければ付けておく + base_dir[j ] = '\\'; + base_dir[j + 1] = 0; + } + j = GetFileAttributes(base_dir); + if ((j == INVALID_FILE_ATTRIBUTES) || !(j & FILE_ATTRIBUTE_DIRECTORY)){ + printf("base-directory is invalid\n"); + return 1; + } + + } else { // 未対応のオプション + printf_cp("invalid option, %s\n", tmp_p - 1); + return 1; + } + + } else { // オプションでなければループから抜ける + break; + } + } + if (i < argc){ + tmp_p = argv[i]; + i++; + // オプション以外ならリカバリ・ファイル + j = copy_path_prefix(recovery_file, MAX_LEN - 20, tmp_p, NULL); // 最大で「.vol32768+32768.par2」が追加される + if (j == 0){ + printf("PAR filename is invalid\n"); + return 1; + } + if ((argv[1][0] == 'c') || (argv[1][0] == 't')){ // 作成なら + // 指定されたファイル名が適切か調べる + if (sanitize_filename(offset_file_name(recovery_file), NULL, 0) != 0){ + printf("PAR filename is invalid\n"); + return 1; + } + // 拡張子が「.par2」以外なら標準の拡張子を追加する + j = (int)wcslen(recovery_file); // 浄化でファイル名が短縮されるかもしれない + if (_wcsicmp(recovery_file + (j - 5), L".par2")) + wcscpy(recovery_file + j, L".par2"); + } else { // 検査や修復なら + j = GetFileAttributes(recovery_file); + if ((j == INVALID_FILE_ATTRIBUTES) || (j & FILE_ATTRIBUTE_DIRECTORY)){ + wchar_t search_path[MAX_LEN], file_ext[EXT_LEN]; + int name_len, dir_len; + HANDLE hFind; + WIN32_FIND_DATA FindData; + // リカバリ・ファイルの名前の基 + get_base_filename(recovery_file, search_path, file_ext); + if (file_ext[0] == 0) + wcscpy(file_ext, L".par2"); // 拡張子が省略されてるなら標準の拡張子を追加してみる + // 先にボリューム番号を含まないファイル名を試す + name_len = (int)wcslen(search_path); + dir_len = (int)(offset_file_name(recovery_file) - recovery_file); + wcscpy(search_path + name_len, file_ext); + hFind = FindFirstFile(search_path, &FindData); + if (hFind != INVALID_HANDLE_VALUE){ + file_ext[0] = 0; + FindClose(hFind); + } + if (file_ext[0] != 0){ // ボリューム番号を含むファイル名を試す + wcscpy(search_path + name_len, L".vol*"); + wcscat(search_path, file_ext); // 「~.par2」を「~.vol*.par2」にする + hFind = FindFirstFile(search_path, &FindData); + if (hFind != INVALID_HANDLE_VALUE){ + do { + name_len = (int)wcslen(FindData.cFileName); + if (dir_len + name_len < MAX_LEN){ // ファイル名が長すぎない + file_ext[0] = 0; + break; // 見つけたファイル名で問題なし + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + } + } + if (file_ext[0] != 0){ // その他のファイル名を試す + wcscpy(search_path + name_len, L"*"); + wcscat(search_path, file_ext); // 「~.par2」を「~*.par2」にする + hFind = FindFirstFile(search_path, &FindData); + if (hFind != INVALID_HANDLE_VALUE){ + file_ext[0] = '.'; // 拡張子を元に戻しておく + do { + //printf("file name = %S\n", FindData.cFileName); + name_len = (int)wcslen(FindData.cFileName); + if (dir_len + name_len < MAX_LEN){ // ファイル名が長すぎない + file_ext[0] = 0; + break; // 見つけたファイル名で問題なし + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + } + } + if (file_ext[0] != 0){ + printf("valid file is not found\n"); + return 1; + } + wcscpy(recovery_file + dir_len, FindData.cFileName); + } + } + } + +/* +// デバッグ用の比較だけなら +if (list2_buf){ + list2_buf[list2_len] = 0; + wcscpy(base_dir, list2_buf + 1); +} else { + base_dir[0] = 0; +} +i = PathMatchWild(uni_buf, base_dir); +printf("text = \"%S\"\n", uni_buf); +printf("wild = \"%S\"\n", base_dir); +printf("result = %d\n", i); +return 0; +*/ + + if (recovery_file[0] == 0){ // リカバリ・ファイルが指定されてないなら + printf("PAR file is not specified\n"); + return 1; + } + + // input file の位置が指定されて無くて、 + // 最初のソース・ファイルが絶対パスで指定されてるなら、それを使う + if ((argv[1][0] == 'c') || (argv[1][0] == 't')){ + if ((base_dir[0] == 0) && ((switch_set & 0x02) == 0) && (i < argc)){ + tmp_p = argv[i]; + if (is_full_path(tmp_p) != 0){ // 絶対パスなら + wchar_t file_path[MAX_LEN]; + if (copy_path_prefix(file_path, MAX_LEN - 2, tmp_p, NULL) == 0){ + printf("base-directory is invalid\n"); + return 1; + } + get_base_dir(file_path, base_dir); // 最初のソース・ファイルの位置にする + if (len_without_prefix(base_dir) == 0) // サブ・ディレクトリが無ければ + base_dir[0] = 0; + } + } + } + if (base_dir[0] == 0) // input file の位置が指定されてないなら + get_base_dir(recovery_file, base_dir); // リカバリ・ファイルの位置にする + base_len = (int)wcslen(base_dir); + + // 検査結果ファイルの位置が指定されてないなら + if (((recent_data != 0) || ((switch_set & 0x20) != 0)) && (ini_path[0] == 0)){ + // 実行ファイルのディレクトリにする + j = GetModuleFileName(NULL, ini_path, MAX_LEN); + if ((j == 0) || (j >= MAX_LEN)){ + printf("GetModuleFileName\n"); + return 1; + } + while (j > 0){ + if (ini_path[j - 1] == '\\'){ + ini_path[j] = 0; + break; + } + j--; + } + if (j >= MAX_LEN - INI_NAME_LEN - 1){ + printf("save-directory is invalid\n"); + return 1; + } + } + + // 環境の表示 + print_environment(); + + switch (argv[1][0]){ + case 'c': + case 't': + // リカバリ・ファイル作成ならソース・ファイルのリストがいる + if (i >= argc){ // もうファイル指定が無いなら + wchar_t search_path[MAX_LEN]; + int dir_len; + // リカバリ・ファイルの拡張子を取り除いたものがソース・ファイルと見なす + wcscpy(search_path, recovery_file); + tmp_p = offset_file_name(search_path); + tmp_p = wcsrchr(tmp_p, '.'); + if (tmp_p != NULL) + *tmp_p = 0; // 拡張子を取り除く + if (_wcsnicmp(base_dir, search_path, base_len) != 0){ // 基準ディレクトリ外のファイルは拒否する + printf_cp("out of base-directory, %s\n", search_path); + return 1; + } + // そのファイルが存在するか確かめる + dir_len = (int)wcslen(search_path) - 2; // ファイル名末尾の「\」を無視して、ディレクトリ部分の長さを求める + while (search_path[dir_len] != '\\') + dir_len--; + dir_len++; + if (search_files(search_path, dir_len, 1, 0)){ // ファイルだけを探す + free(list_buf); + return 1; + } + if (file_num != 1){ // ファイルが見つかったか確かめる + free(list_buf); + printf_cp("input file is not found, %s\n", search_path + base_len); + return 1; + } + } else if (switch_set & 0x06){ // ファイル・リストの読み込み + j = (switch_set & 0x04) ? CP_UTF8 : CP_OEMCP; + if (read_list(argv[i], j)){ + free(list_buf); + return 1; + } + } else { // 入力ファイルの指定 + wchar_t search_path[MAX_LEN]; + int dir_len; + +/* + if (list2_buf){ + // デバッグ用のリスト表示 + printf("filtering list :\n", list2_buf); + j = 0; + while (j < list2_len){ + printf(" \"%S\"\n", list2_buf + j); + j += wcslen(list2_buf + j) + 1; + } + printf("length : %d, allow number = %d\n\n", list2_len, list2_max); + } +*/ + + for (; i < argc; i++){ + // ファイルが基準ディレクトリ以下に存在することを確認する + tmp_p = argv[i]; + j = copy_path_prefix(search_path, MAX_LEN - ADD_LEN - 2, tmp_p, base_dir); // 絶対パスにしてから比較する + if (j == 0){ + free(list_buf); + printf_cp("filename is invalid, %s\n", tmp_p); + return 1; + } + if ((j <= base_len) || (_wcsnicmp(base_dir, search_path, base_len) != 0)){ // 基準ディレクトリ外なら + free(list_buf); + printf_cp("out of base-directory, %s\n", tmp_p); + return 1; + } + //printf("%d = \"%S\"\n", i, argv[i]); + //printf_cp("search = \"%s\"\n", search_path); + // 「*」や「?」で検索しない場合、ファイルが見つからなければエラーにする + j = -1; + if (wcspbrk(search_path + base_len, L"*?") == NULL) + j = file_num; + // ファイルを検索する + dir_len = (int)wcslen(search_path) - 2; // ファイル名末尾の「\」を無視して、ディレクトリ部分の長さを求める + while (search_path[dir_len] != '\\') + dir_len--; + dir_len++; + if (search_files(search_path, dir_len, switch_v & 8, j)){ + free(list_buf); + return 1; + } + // ファイルが見つかったか確かめる (すでに登録済みならいい) + if ((j != -1) && (j == file_num) && + (search_file_path(list_buf, list_len, search_path + base_len) == 0)){ + free(list_buf); + printf_cp("input file is not found, %s\n", search_path + base_len); + return 1; + } + } + } + if (list2_buf){ // 除外ファイル・リストを消去する + free(list2_buf); + list2_buf = NULL; + } +/*{ +FILE *fp; +fp = fopen("list_buf.txt", "wb"); +fwrite(list_buf, 2, list_len, fp); +fclose(fp); +}*/ + if (file_num < 1){ + free(list_buf); + printf("input file is not found\n"); + return 1; + } + if (file_num > MAX_SOURCE_NUM){ + free(list_buf); + printf("too many input files %d\n", file_num); + return 1; + } + if (parity_num == 0) // リカバリ・ファイルを作らない場合は、必ずインデックス・ファイルを作る + switch_set &= ~0x01; + if (check_recovery_match(switch_set & 0x01)){ + free(list_buf); + return 1; + } + + if (total_file_size == 0){ // ファイル・サイズが全て 0 だとブロックも無いはず + block_size = 0; + source_num = 0; + parity_num = 0; + } else { // ブロック数を計算する + // 最適なブロック・サイズを調べる + i = 0; + if (block_size != 0){ // ブロック・サイズを指定 + i = block_size; + } else { // ブロック・サイズとブロック数の割合を指定 + i = ((unsigned int)switch_b >> 22) * -10; // 割合を 0.01% 刻みにする + } + j = 0; // 標準ではソース・ブロック数を制限しない + if (source_num != 0){ // ブロック数を指定 + j = source_num; + } else if (i == 0){ // 全てを指定してなければ + i = -100; // 割合で 1% にする + j = 3000; // 3000ブロックまでにする + } + if (check_block_size(switch_b & 0x003FFFFC, i, j)){ + free(list_buf); + return 1; + } + + if (parity_num <= -200000){ // 復元できるファイル数でパリティ・ブロック数を決める + j = calc_required_parity(parity_num * -1 - 200000); + if (j < 0){ + free(list_buf); + return 1; + } + parity_num = j; + } else if (parity_num < 0){ // 冗長性(%)でパリティ・ブロック数を決める + parity_num = -parity_num; // % の 100倍になってることに注意 + i = (int)(((unsigned int)parity_num * (unsigned int)source_num) / 10000); + j = (i * 10000) / source_num; + //printf("num0 = %d, rate = %d, target = %d \n", i, j, parity_num); + if (j != parity_num){ + j = ((i + 1) * 10000) / source_num; + //printf("num1 = %d, rate = %d, target = %d \n", i + 1, j, parity_num); + if (j == parity_num) + i++; // 1個増やして冗長性を一致させる + } + if (i > MAX_PARITY_NUM) + i = MAX_PARITY_NUM; + if (i == 0) + i = 1; + parity_num = i; + } + } + // 分割サイズはブロック・サイズの倍数にする + if (split_size >= 4){ + if (split_size <= block_size){ + split_size = block_size; + } else { + split_size -= split_size % block_size; + } + } + // リカバリ・ファイルの数とその最大ブロック数を計算する + if (parity_num > 0){ + if (recovery_limit == 0){ // 制限値が未設定ならパリティ・ブロック数にする + recovery_limit = parity_num; + } else if (recovery_limit < 0){ // ソース・ファイルの最大ブロック数を制限値にする + recovery_limit = -recovery_limit; + if (split_size >= 4){ // ソース・ファイルを分割する場合はその分割サイズまで + j = split_size / block_size; // 分割ファイル内のブロック数 + if (recovery_limit > j) + recovery_limit = j; + } + } + // パリティ・ブロックをリカバリ・ファイルに分配する方法 + i = (switch_set & 0x30000) >> 16; + if (i == 0){ // 同じ量ずつ割り振る + if (recovery_num == 0){ // 未設定なら、ソース・ファイル数と冗長性から計算する + recovery_num = entity_num * parity_num / source_num; + if (recovery_num < 1){ + recovery_num = 1; + } else if (recovery_num > 10){ + recovery_num = 10; // QuickPar と同じく 10個までにする + } + } + if (recovery_num > parity_num) + recovery_num = parity_num; + // 制限数から最低ファイル数を計算する + j = (parity_num + recovery_limit - 1) / recovery_limit; + if (recovery_num < j) + recovery_num = j; + } else if (i == 1){ // 倍々で異なる量にする + if (recovery_num == 0){ // 未設定なら、ソース・ファイル数と冗長性から計算する + recovery_num = entity_num * parity_num / source_num; + if (recovery_num < 3){ + if (parity_num >= 7){ + recovery_num = 3; // 大中小で 3個にする。 + } else if (parity_num >= 3){ + recovery_num = 2; // 大小で 2個にする。 + } else { + recovery_num = 1; + } + } else if (recovery_num > 10){ + recovery_num = 10; // QuickPar と同じく 10個までにする + } + } + // 基準値が1の場合のファイル数 = 最大ファイル数 + i = 1; // recovery_num + j = 1; // total count + k = 1; // count in the file + while (j < parity_num){ + k *= 2; + if (k > recovery_limit){ + i += (parity_num - j + recovery_limit - 1) / recovery_limit; + break; + } + j += k; + i++; + } + //printf("recovery_num = %d, max = %d (%d blocks)\n", recovery_num, i, k); + if (recovery_num > i) + recovery_num = i; + i = (1 << recovery_num) - 1; // 分割数 + k = (parity_num + i - 1) / i; // 倍率 + //printf("recovery_num = %d, split = %d, base = %d\n", recovery_num, i, k); + if (recovery_limit < parity_num){ + if (recovery_limit * recovery_num < parity_num){ // ファイル数が少なすぎるなら増やす + recovery_num = (parity_num + recovery_limit - 1) / recovery_limit; + //printf("min = %d, * %d = %d\n", recovery_num, recovery_limit, recovery_limit * recovery_num); + } + i = parity_num; + j = recovery_num; + if (j > 16){ // 限界を超えてる分は省く + i -= recovery_limit * (j - 16); + j = 16; + } + k = (1 << j) - 1; // 分割数 + k = (i + k - 1) / k; // 倍率 + //printf("rest = %d blocks on %d files, base = %d, last = %d, %d, limit = %d\n", i, j, k, k << (j - 2), i - ((1 << (j - 1)) - 1) * k, recovery_limit); + while ((j >= 2) && (k < recovery_limit) && (((k << (j - 2)) > recovery_limit) || (i - ((1 << (j - 1)) - 1) * k > recovery_limit))){ + i -= recovery_limit; + j--; + k = (1 << j) - 1; // 分割数 + k = (i + k - 1) / k; // 倍率 + //printf("rest = %d blocks on %d files, base = %d, last = %d, %d\n", i, j, k, k << (j - 2), i - ((1 << (j - 1)) - 1) * k); + } + } + recovery_limit = (recovery_limit & 0xFFFF) | (k << 16); + } else { + if (i == 2){ // 1,2,4,8,16 と2の乗数にする + recovery_num = 0; + i = 1; // exp_num + j = 0; // total count + k = 0; // count in the file + while (j < parity_num){ + k = i; + if (k >= recovery_limit){ // サイズが制限されてるなら + k = recovery_limit; + } else { + i *= 2; + } + j += k; + recovery_num++; + } + recovery_limit = k; + } else { // 1,1,2,5,10,10,20,50 という decimal weights sizing scheme にする + recovery_num = 0; + i = 1; // exp_num + j = 0; // total count + k = 0; // count in the file + while (j < parity_num){ + k = i; + if (k >= recovery_limit){ // サイズが制限されてるなら + k = recovery_limit; + } else { + switch (recovery_num % 4){ + case 1: + case 3: + i = i * 2; + break; + case 2: + i = (i / 2) * 5; + break; + } + } + j += k; + recovery_num++; + } + recovery_limit = k; + } + } + } else { + recovery_num = 0; + } + printf("Input File count\t: %d\n", file_num); + printf("Input File total size\t: %I64d\n", total_file_size); + printf("Input File Slice size\t: %u\n", block_size); + printf("Input File Slice count\t: %d\n", source_num); + printf("Recovery Slice count\t: %d\n", parity_num); + if (first_num > 0) + printf("Recovery Slice start\t: %d\n", first_num); + if (source_num != 0){ + i = (10000 * parity_num) / source_num; + } else { + i = 0; + } + printf("Redundancy rate\t\t: %d.%02d%%\n", i / 100, i % 100); + printf("Recovery File count\t: %d\n", recovery_num); // Index File の分は含まない + if (parity_num > 0){ + i = (switch_set & 0x30000) >> 16; + printf("Slice distribution\t: %d, ", i); + switch (i){ + case 0: + printf("uniform (until %d)\n", recovery_limit); + break; + case 1: + printf("variable (base %d until %d)\n", (unsigned int)recovery_limit >> 16, recovery_limit & 0xFFFF); + break; + case 2: + printf("power of two (until %d)\n", recovery_limit); + break; + case 3: + printf("decimal weights (until %d)\n", recovery_limit); + break; + } + k = (switch_set & 0x0700) >> 8; + printf("Packet Repetition limit\t: %d\n", k); + } + if (split_size == 1){ // 書庫にリカバリ・レコードを追加できるか + if ((file_num != 1) || (total_file_size < 54)){ + split_size = 0; // zip's min = 100-byte, 7z's min = 74-byte + } else { // 拡張子を調べる + j = (int)wcslen(list_buf); + if ((_wcsicmp(list_buf + j - 4, L".zip") != 0) && (_wcsicmp(list_buf + j - 3, L".7z") != 0)){ + split_size = 0; + } else { + printf_cp("Append recovery record\t: \"%s\"\n", list_buf); + } + } + } else if (split_size >= 4){ // 分割サイズは指定された時だけ表示する + if (total_file_size == 0){ + split_size = 0; + } else { + printf("Split size\t\t: %u\n", split_size); + } + } + if (parity_num + first_num > MAX_PARITY_NUM){ // ブロック数の制限 + free(list_buf); + printf("too many recovery blocks %d\n", parity_num + first_num); + return 1; + } + j = (switch_set & 0x01) | ((switch_set & 0x08) >> 2); + if (argv[1][0] == 'c'){ + i = par2_create(uni_buf, (switch_set & 0x0700) >> 8, (switch_set & 0x70000) >> 16, j); + } else { + i = par2_trial(uni_buf, (switch_set & 0x0700) >> 8, (switch_set & 0x70000) >> 16, j); + } + break; + case 'v': + case 'r': + // 検査・修復なら外部の検査対象ファイルのリストがあるかもしれない + if (i < argc){ // 外部のファイルが指定されてるなら + wchar_t search_path[MAX_LEN]; + int dir_len; + list2_len = 0; + list2_max = ALLOC_LEN; + list2_buf = (wchar_t *)malloc(list2_max * 2); + if (list2_buf == NULL){ + printf("malloc, %d\n", list2_max * 2); + return 1; + } + + if (switch_set & 0x06){ // ファイル・リストの読み込み + j = (switch_set & 0x04) ? CP_UTF8 : CP_OEMCP; + if (read_external_list(argv[i], j)) + list2_len = 0; + i = argc; // それ以上読み込まない + } + + for (; i < argc; i++){ + j = copy_path_prefix(search_path, MAX_LEN, argv[i], NULL); + if (j == 0){ + free(list2_buf); + printf_cp("filename is invalid, %s\n", argv[i]); + return 1; + } + //printf_cp("external file, %s\n", search_path); + dir_len = (int)wcslen(search_path) - 2; // ファイル名末尾の「\」を無視して、ディレクトリ部分の長さを求める + while (search_path[dir_len] != '\\') + dir_len--; + dir_len++; + // 「*」や「?」で検索しない場合、ファイルが見つからなければエラーにする + j = -1; + if (wcspbrk(search_path + dir_len, L"*?") == NULL) + j = list2_len; + if (search_external_files(search_path, dir_len, j)){ + free(list2_buf); + return 1; + } + if ((j != -1) && (j == list2_len)){ // ファイルが見つかったか確かめる + free(list2_buf); + printf_cp("external file is not found, %s\n", search_path); + return 1; + } + } +/*{ +FILE *fp; +fp = fopen("list2_buf.txt", "wb"); +fwrite(list2_buf, 2, list2_len, fp); +fclose(fp); +}*/ + if (list2_len == 0){ // 有効なファイルが見つからなかった場合 + free(list2_buf); + list2_buf = NULL; + } + } + if (switch_set & 0x20) + json_open(); // 検査結果を JSONファイルに書き込む + if (argv[1][0] == 'v'){ + i = par2_verify(uni_buf); + } else { + i = par2_repair(uni_buf); + } + json_close(); + if (list2_buf) + free(list2_buf); + break; + case 'l': + i = par2_list(uni_buf, (switch_set & 0x10) >> 4); + break; + } + + if (list_buf); + free(list_buf); + //printf("ExitCode: 0x%02X\n", i); + return i; +} + diff --git a/source/par2j/par2j.vcxproj b/source/par2j/par2j.vcxproj new file mode 100644 index 0000000..b60c2f3 --- /dev/null +++ b/source/par2j/par2j.vcxproj @@ -0,0 +1,165 @@ + + + + + Release + Win32 + + + Release + x64 + + + + {3DD6B39E-7178-4E46-A3DE-17DE984DF86B} + par2j + Win32Proj + + + + Application + v143 + Unicode + true + + + Application + v143 + Unicode + true + false + + + + + + + + + + + + + <_ProjectFileVersion>16.0.31025.104 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + false + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + false + false + + + + MaxSpeed + OnlyExplicitInline + true + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + false + true + StreamingSIMDExtensions2 + false + + Level3 + + $(ProjectDir)/OpenCL + + + imagehlp.lib;%(AdditionalDependencies) + false + Console + true + true + true + true + MachineX86 + UseLinkTimeCodeGeneration + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + false + true + false + + Level3 + + $(ProjectDir)/OpenCL + + + imagehlp.lib;%(AdditionalDependencies) + false + Console + true + true + true + true + MachineX64 + UseLinkTimeCodeGeneration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/par2j/phmd5.c b/source/par2j/phmd5.c new file mode 100644 index 0000000..786a611 --- /dev/null +++ b/source/par2j/phmd5.c @@ -0,0 +1,109 @@ +/*---------------------------------------------------------------------------- +; +; MD5 hash generator -- Paul Houle (paulhoule.com) 11/13/2017 +; +; Non-time critical C logic. All API entry points are here. +; See phmd5.h for documentation. +; +;---------------------------------------------------------------------------*/ + +#include +#include "phmd5.h" + +// First call -- initialize pmd5 structure for use. +void Phmd5Begin(PHMD5 *pmd5) { + unsigned __int32 *uhash = (unsigned __int32 *) pmd5->hash; + + uhash[0] = 0x67452301; // init hash per rfc1321 + uhash[1] = 0xEFCDAB89; + uhash[2] = 0x98BADCFE; + uhash[3] = 0x10325476; + + pmd5->totbyt = 0; // init count of data bytes processed +} + +// Last call -- after this, pmd5->hash holds final MD5 hash. +void Phmd5End(PHMD5 *pmd5) { + char pad[72]; // pad buffer (worst case is 72 bytes) + unsigned padc; // size of needed pad (9-72 bytes) + + padc = 64 - ((unsigned) pmd5->totbyt & 63); // pad to 64-byte boundary + if (padc < 9) padc += 64; // add a block if we need more room + memset(pad, 0, padc); // clear entire pad area + pad[0] = (char) 0x80; // place input stream terminator + // place 64-bit input data bit count + *(unsigned __int64 *) &pad[padc - 8] = pmd5->totbyt << 3; + Phmd5Process(pmd5, pad, padc); // process the pad +} + +// Work done here -- call for as many input blocks that need to be processed. +// pdata points to the input data, bytecnt is pdata size (0..n bytes). +// See phmd5.h regarding how to use this optimally. +void Phmd5Process(PHMD5 *pmd5, char *pdata, size_t bytecnt) { + unsigned resid = (unsigned) pmd5->totbyt; + + pmd5->totbyt += bytecnt; // update total bytes processed + + resid &= 63; // count of bytes now in pmd5->buf + + // This block handles the case of residual data in pmd5->buf. + // After this block pmd5->buf is empty (except perhaps on exit). + + if (resid) { // if residual exists, + unsigned cb = 64 - resid; + if (cb > bytecnt) cb = (unsigned) bytecnt; + memcpy(pmd5->buf + resid, pdata, cb); + pdata += cb; + bytecnt -= cb; + if (resid + cb < 64) return; + Phmd5DoBlocks(pmd5->hash, pmd5->buf, 64); + } + + // This block processes input data in-place, if the data is dword + // aligned and in 64-byte chunks. + + if ((unsigned) bytecnt & ~63 && ((size_t) pdata & 3) == 0) { + Phmd5DoBlocks(pmd5->hash, pdata, bytecnt & ~63); + pdata += bytecnt & ~63; + bytecnt &= 63; + } + + while (bytecnt) { // handle residual/non-aligned data + unsigned cb = 64 > (unsigned) bytecnt ? (unsigned) bytecnt : 64; + memcpy(pmd5->buf, pdata, cb); + pdata += cb; + bytecnt -= cb; + if (cb < 64) return; + Phmd5DoBlocks(pmd5->hash, pmd5->buf, 64); + }; +} + +// Added by Yutaka Sawada for PAR2's padding null bytes at the last of each file. +void Phmd5ProcessZero(PHMD5 *pmd5, size_t bytecnt) { + unsigned resid = (unsigned) pmd5->totbyt; + + pmd5->totbyt += bytecnt; // update total bytes processed + + resid &= 63; // count of bytes now in pmd5->buf + + // This block handles the case of residual data in pmd5->buf. + // After this block pmd5->buf is empty (except perhaps on exit). + + if (resid) { // if residual exists, + unsigned cb = 64 - resid; + if (cb > bytecnt) cb = (unsigned) bytecnt; + memset(pmd5->buf + resid, 0, cb); + bytecnt -= cb; + if (resid + cb < 64) return; + Phmd5DoBlocks(pmd5->hash, pmd5->buf, 64); + } + + // This block processes input data in-place, if the data is in 64-byte chunks. + + if (bytecnt & ~63) { + Phmd5DoBlocksZero(pmd5->hash, bytecnt & ~63); + bytecnt &= 63; + } + + if (bytecnt) memset(pmd5->buf, 0, 64); // handle residual data +} diff --git a/source/par2j/phmd5.h b/source/par2j/phmd5.h new file mode 100644 index 0000000..3068259 --- /dev/null +++ b/source/par2j/phmd5.h @@ -0,0 +1,66 @@ +/*---------------------------------------------------------------------------- +; +; MD5 hash generator -- Paul Houle (paulhoule.com) 11/13/2017 +; +; This code is in the public domain. Please attribute the author. +; +; There are a lot of MD5 generators; here's another. This one targets a +; little-endian memory architecture only (eg X86). The benefit of this +; is speed -- bytes within larger elements never need to be reversed, +; which means the source data can be processed in-place. +; +; Though other compilers might be usable, this was developed using +; Microsoft 32/64-bit C 12.0 [Version 18.00.30723]. Vendor specific +; definitions (eg. _rotl, __int32, __int64) are used. +; Build commands: +; +; cl /c /Ox phmd5.c +; cl /c /Ox phmd5a.c +; +; Link the resulting .obj's into your executable and #include "phmd5.h" +; +; How to call the routines to generate a hash: +; +; (1) Allocate a PHMD5 type struct -- it's small, can be static or local. +; A pointer to this struct is the first argument to all functions. +; +; (2) Call Phmd5Begin() once -- this initializes the PHMD5 struct. +; +; (3) Call Phmd5Process() as many times as necessary for all data +; to be included in the MD5 hash. +; +; (4) Call Phmd5End() once. The final 16-byte MD5 hash will then be +; available in PHMD5->hash. Note the finished hash is a simple array +; of bytes, and must be treated/displayed/copied/etc that way. +; +; For best performance the Phmd5Process() "pdata" pointer should be 32-bit +; aligned (a multiple of 4) and "bytecnt" should be a multiple of 64. +; As long as both of these conditions continue to be met the input data is +; processed in-place; otherwise, some speed (10-15%) is lost as the data +; is copied to an internal blocking buffer before being proceessed. +; +;---------------------------------------------------------------------------*/ + +#ifndef _PHMD5_DEFINED // include guard +#define _PHMD5_DEFINED + +#include +typedef struct { + unsigned char hash[16]; // final 16-byte hash winds up here + unsigned __int64 totbyt; // processed byte count + char buf[64]; // input blocking buffer +} PHMD5; + +void Phmd5Begin(PHMD5 *pmd5); +void Phmd5Process(PHMD5 *pmd5, char *pdata, size_t bytecnt); +void Phmd5End(PHMD5 *pmd5); +void Phmd5DoBlocks(unsigned char *hash, char *pdata, size_t bytecnt); + +// added by Yutaka Sawada for PAR2 +void Phmd5ProcessZero(PHMD5 *pmd5, size_t bytecnt); +void Phmd5DoBlocksZero(unsigned char *hash, size_t bytecnt); + +// calculate two MD5 at once for PAR2 +void Phmd5Process2(PHMD5 *pmd5, PHMD5 *pmd52, char *pdata, size_t bytecnt); + +#endif diff --git a/source/par2j/phmd5a.c b/source/par2j/phmd5a.c new file mode 100644 index 0000000..1978daf --- /dev/null +++ b/source/par2j/phmd5a.c @@ -0,0 +1,480 @@ +/*---------------------------------------------------------------------------- +; +; MD5 hash generator -- Paul Houle (paulhoule.com) 11/13/2017 +; +; Called only from phmd5.c -- see phmd5.h for overview +; +; This is the same logic in phmd5a.asm, implemented (after the fact) in C. +; The C compiler must support the "_rotl()" function generated inline to +; achieve maximal performance. +; +; It turns out the MSFT C compiler, coupled with newer processor, results +; in timings comparable to the 32-bit only phmd5a.asm hand-written assembly. +; Therefore this "C" implementation is now used. +; Avoiding assembly allows the code to be compiled either 32 or 64 bit. +; +; Note that a "little-endian" memory architecture is assumed. +; +; The Fx() and MD5STEP() macros were written by Colin Plumb in 1993. +; MD5STEP() was changed slightly to match how phmd5a.asm operates. +; +;---------------------------------------------------------------------------*/ + +/* +#include // for _rotl() +#include "phmd5.h" + + +// MD5 Optimisation Tricks by Anime Tosho +// https://github.com/animetosho/md5-optimisation#optimisation-tricks-single-buffer +// Dependency shortcut in G function +#define F1(x, y, z) ((x & y) + (~x & z)) + +//#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +#define MD5STEP(f, w, x, y, z, ix, s, sc) \ + w = _rotl(w + f(x, y, z) + ((unsigned *) pdata)[ix] + sc, s) + x + +void Phmd5DoBlocks( + unsigned char *hash, + char *pdata, + size_t bytecnt +) { + unsigned __int32 a = *(unsigned __int32 *) &hash[ 0]; + unsigned __int32 b = *(unsigned __int32 *) &hash[ 4]; + unsigned __int32 c = *(unsigned __int32 *) &hash[ 8]; + unsigned __int32 d = *(unsigned __int32 *) &hash[12]; + + do { + MD5STEP(F1, a, b, c, d, 0, 7, 0xd76aa478); + MD5STEP(F1, d, a, b, c, 1, 12, 0xe8c7b756); + MD5STEP(F1, c, d, a, b, 2, 17, 0x242070db); + MD5STEP(F1, b, c, d, a, 3, 22, 0xc1bdceee); + MD5STEP(F1, a, b, c, d, 4, 7, 0xf57c0faf); + MD5STEP(F1, d, a, b, c, 5, 12, 0x4787c62a); + MD5STEP(F1, c, d, a, b, 6, 17, 0xa8304613); + MD5STEP(F1, b, c, d, a, 7, 22, 0xfd469501); + MD5STEP(F1, a, b, c, d, 8, 7, 0x698098d8); + MD5STEP(F1, d, a, b, c, 9, 12, 0x8b44f7af); + MD5STEP(F1, c, d, a, b, 10, 17, 0xffff5bb1); + MD5STEP(F1, b, c, d, a, 11, 22, 0x895cd7be); + MD5STEP(F1, a, b, c, d, 12, 7, 0x6b901122); + MD5STEP(F1, d, a, b, c, 13, 12, 0xfd987193); + MD5STEP(F1, c, d, a, b, 14, 17, 0xa679438e); + MD5STEP(F1, b, c, d, a, 15, 22, 0x49b40821); + + MD5STEP(F2, a, b, c, d, 1, 5, 0xf61e2562); + MD5STEP(F2, d, a, b, c, 6, 9, 0xc040b340); + MD5STEP(F2, c, d, a, b, 11, 14, 0x265e5a51); + MD5STEP(F2, b, c, d, a, 0, 20, 0xe9b6c7aa); + MD5STEP(F2, a, b, c, d, 5, 5, 0xd62f105d); + MD5STEP(F2, d, a, b, c, 10, 9, 0x02441453); + MD5STEP(F2, c, d, a, b, 15, 14, 0xd8a1e681); + MD5STEP(F2, b, c, d, a, 4, 20, 0xe7d3fbc8); + MD5STEP(F2, a, b, c, d, 9, 5, 0x21e1cde6); + MD5STEP(F2, d, a, b, c, 14, 9, 0xc33707d6); + MD5STEP(F2, c, d, a, b, 3, 14, 0xf4d50d87); + MD5STEP(F2, b, c, d, a, 8, 20, 0x455a14ed); + MD5STEP(F2, a, b, c, d, 13, 5, 0xa9e3e905); + MD5STEP(F2, d, a, b, c, 2, 9, 0xfcefa3f8); + MD5STEP(F2, c, d, a, b, 7, 14, 0x676f02d9); + MD5STEP(F2, b, c, d, a, 12, 20, 0x8d2a4c8a); + + MD5STEP(F3, a, b, c, d, 5, 4, 0xfffa3942); + MD5STEP(F3, d, a, b, c, 8, 11, 0x8771f681); + MD5STEP(F3, c, d, a, b, 11, 16, 0x6d9d6122); + MD5STEP(F3, b, c, d, a, 14, 23, 0xfde5380c); + MD5STEP(F3, a, b, c, d, 1, 4, 0xa4beea44); + MD5STEP(F3, d, a, b, c, 4, 11, 0x4bdecfa9); + MD5STEP(F3, c, d, a, b, 7, 16, 0xf6bb4b60); + MD5STEP(F3, b, c, d, a, 10, 23, 0xbebfbc70); + MD5STEP(F3, a, b, c, d, 13, 4, 0x289b7ec6); + MD5STEP(F3, d, a, b, c, 0, 11, 0xeaa127fa); + MD5STEP(F3, c, d, a, b, 3, 16, 0xd4ef3085); + MD5STEP(F3, b, c, d, a, 6, 23, 0x04881d05); + MD5STEP(F3, a, b, c, d, 9, 4, 0xd9d4d039); + MD5STEP(F3, d, a, b, c, 12, 11, 0xe6db99e5); + MD5STEP(F3, c, d, a, b, 15, 16, 0x1fa27cf8); + MD5STEP(F3, b, c, d, a, 2, 23, 0xc4ac5665); + + MD5STEP(F4, a, b, c, d, 0, 6, 0xf4292244); + MD5STEP(F4, d, a, b, c, 7, 10, 0x432aff97); + MD5STEP(F4, c, d, a, b, 14, 15, 0xab9423a7); + MD5STEP(F4, b, c, d, a, 5, 21, 0xfc93a039); + MD5STEP(F4, a, b, c, d, 12, 6, 0x655b59c3); + MD5STEP(F4, d, a, b, c, 3, 10, 0x8f0ccc92); + MD5STEP(F4, c, d, a, b, 10, 15, 0xffeff47d); + MD5STEP(F4, b, c, d, a, 1, 21, 0x85845dd1); + MD5STEP(F4, a, b, c, d, 8, 6, 0x6fa87e4f); + MD5STEP(F4, d, a, b, c, 15, 10, 0xfe2ce6e0); + MD5STEP(F4, c, d, a, b, 6, 15, 0xa3014314); + MD5STEP(F4, b, c, d, a, 13, 21, 0x4e0811a1); + MD5STEP(F4, a, b, c, d, 4, 6, 0xf7537e82); + MD5STEP(F4, d, a, b, c, 11, 10, 0xbd3af235); + MD5STEP(F4, c, d, a, b, 2, 15, 0x2ad7d2bb); + MD5STEP(F4, b, c, d, a, 9, 21, 0xeb86d391); + + a += *(unsigned __int32 *) &hash[ 0]; + b += *(unsigned __int32 *) &hash[ 4]; + c += *(unsigned __int32 *) &hash[ 8]; + d += *(unsigned __int32 *) &hash[12]; + + *(unsigned __int32 *) &hash[ 0] = a; + *(unsigned __int32 *) &hash[ 4] = b; + *(unsigned __int32 *) &hash[ 8] = c; + *(unsigned __int32 *) &hash[12] = d; + + pdata += 64; + } while (bytecnt -= 64); +} + +#undef MD5STEP + +// for update with null bytes +#define MD5STEP(f, w, x, y, z, ix, s, sc) \ + w = _rotl(w + f(x, y, z) + sc, s) + x + +void Phmd5DoBlocksZero( + unsigned char *hash, + size_t bytecnt +) { + unsigned __int32 a = *(unsigned __int32 *) &hash[ 0]; + unsigned __int32 b = *(unsigned __int32 *) &hash[ 4]; + unsigned __int32 c = *(unsigned __int32 *) &hash[ 8]; + unsigned __int32 d = *(unsigned __int32 *) &hash[12]; + + do { + MD5STEP(F1, a, b, c, d, 0, 7, 0xd76aa478); + MD5STEP(F1, d, a, b, c, 1, 12, 0xe8c7b756); + MD5STEP(F1, c, d, a, b, 2, 17, 0x242070db); + MD5STEP(F1, b, c, d, a, 3, 22, 0xc1bdceee); + MD5STEP(F1, a, b, c, d, 4, 7, 0xf57c0faf); + MD5STEP(F1, d, a, b, c, 5, 12, 0x4787c62a); + MD5STEP(F1, c, d, a, b, 6, 17, 0xa8304613); + MD5STEP(F1, b, c, d, a, 7, 22, 0xfd469501); + MD5STEP(F1, a, b, c, d, 8, 7, 0x698098d8); + MD5STEP(F1, d, a, b, c, 9, 12, 0x8b44f7af); + MD5STEP(F1, c, d, a, b, 10, 17, 0xffff5bb1); + MD5STEP(F1, b, c, d, a, 11, 22, 0x895cd7be); + MD5STEP(F1, a, b, c, d, 12, 7, 0x6b901122); + MD5STEP(F1, d, a, b, c, 13, 12, 0xfd987193); + MD5STEP(F1, c, d, a, b, 14, 17, 0xa679438e); + MD5STEP(F1, b, c, d, a, 15, 22, 0x49b40821); + + MD5STEP(F2, a, b, c, d, 1, 5, 0xf61e2562); + MD5STEP(F2, d, a, b, c, 6, 9, 0xc040b340); + MD5STEP(F2, c, d, a, b, 11, 14, 0x265e5a51); + MD5STEP(F2, b, c, d, a, 0, 20, 0xe9b6c7aa); + MD5STEP(F2, a, b, c, d, 5, 5, 0xd62f105d); + MD5STEP(F2, d, a, b, c, 10, 9, 0x02441453); + MD5STEP(F2, c, d, a, b, 15, 14, 0xd8a1e681); + MD5STEP(F2, b, c, d, a, 4, 20, 0xe7d3fbc8); + MD5STEP(F2, a, b, c, d, 9, 5, 0x21e1cde6); + MD5STEP(F2, d, a, b, c, 14, 9, 0xc33707d6); + MD5STEP(F2, c, d, a, b, 3, 14, 0xf4d50d87); + MD5STEP(F2, b, c, d, a, 8, 20, 0x455a14ed); + MD5STEP(F2, a, b, c, d, 13, 5, 0xa9e3e905); + MD5STEP(F2, d, a, b, c, 2, 9, 0xfcefa3f8); + MD5STEP(F2, c, d, a, b, 7, 14, 0x676f02d9); + MD5STEP(F2, b, c, d, a, 12, 20, 0x8d2a4c8a); + + MD5STEP(F3, a, b, c, d, 5, 4, 0xfffa3942); + MD5STEP(F3, d, a, b, c, 8, 11, 0x8771f681); + MD5STEP(F3, c, d, a, b, 11, 16, 0x6d9d6122); + MD5STEP(F3, b, c, d, a, 14, 23, 0xfde5380c); + MD5STEP(F3, a, b, c, d, 1, 4, 0xa4beea44); + MD5STEP(F3, d, a, b, c, 4, 11, 0x4bdecfa9); + MD5STEP(F3, c, d, a, b, 7, 16, 0xf6bb4b60); + MD5STEP(F3, b, c, d, a, 10, 23, 0xbebfbc70); + MD5STEP(F3, a, b, c, d, 13, 4, 0x289b7ec6); + MD5STEP(F3, d, a, b, c, 0, 11, 0xeaa127fa); + MD5STEP(F3, c, d, a, b, 3, 16, 0xd4ef3085); + MD5STEP(F3, b, c, d, a, 6, 23, 0x04881d05); + MD5STEP(F3, a, b, c, d, 9, 4, 0xd9d4d039); + MD5STEP(F3, d, a, b, c, 12, 11, 0xe6db99e5); + MD5STEP(F3, c, d, a, b, 15, 16, 0x1fa27cf8); + MD5STEP(F3, b, c, d, a, 2, 23, 0xc4ac5665); + + MD5STEP(F4, a, b, c, d, 0, 6, 0xf4292244); + MD5STEP(F4, d, a, b, c, 7, 10, 0x432aff97); + MD5STEP(F4, c, d, a, b, 14, 15, 0xab9423a7); + MD5STEP(F4, b, c, d, a, 5, 21, 0xfc93a039); + MD5STEP(F4, a, b, c, d, 12, 6, 0x655b59c3); + MD5STEP(F4, d, a, b, c, 3, 10, 0x8f0ccc92); + MD5STEP(F4, c, d, a, b, 10, 15, 0xffeff47d); + MD5STEP(F4, b, c, d, a, 1, 21, 0x85845dd1); + MD5STEP(F4, a, b, c, d, 8, 6, 0x6fa87e4f); + MD5STEP(F4, d, a, b, c, 15, 10, 0xfe2ce6e0); + MD5STEP(F4, c, d, a, b, 6, 15, 0xa3014314); + MD5STEP(F4, b, c, d, a, 13, 21, 0x4e0811a1); + MD5STEP(F4, a, b, c, d, 4, 6, 0xf7537e82); + MD5STEP(F4, d, a, b, c, 11, 10, 0xbd3af235); + MD5STEP(F4, c, d, a, b, 2, 15, 0x2ad7d2bb); + MD5STEP(F4, b, c, d, a, 9, 21, 0xeb86d391); + + a += *(unsigned __int32 *) &hash[ 0]; + b += *(unsigned __int32 *) &hash[ 4]; + c += *(unsigned __int32 *) &hash[ 8]; + d += *(unsigned __int32 *) &hash[12]; + + *(unsigned __int32 *) &hash[ 0] = a; + *(unsigned __int32 *) &hash[ 4] = b; + *(unsigned __int32 *) &hash[ 8] = c; + *(unsigned __int32 *) &hash[12] = d; + + } while (bytecnt -= 64); +} + +#undef MD5STEP + +*/ + + +// SIMD function by Yutaka Sawada 2021-02-04 + +#include +#include // for MMX ~ SSE2 +#include "phmd5.h" + + +// delays `x` dependency by Anime Tosho + +//#define F1(x, y, z) (((y ^ z) & x) ^ z) +#define F1(x, y, z) _mm_xor_si128(_mm_and_si128(_mm_xor_si128(y, z), x), z) + +//#define F2(x, y, z) ((z & x) + (~z & y)) +#define F2(x, y, z) _mm_or_si128(_mm_and_si128(x, z), _mm_andnot_si128(z, y)) + +//#define F3(x, y, z) (x ^ y ^ z) +#define F3(x, y, z) _mm_xor_si128(x, _mm_xor_si128(y, z)) + +//#define F4(x, y, z) (y ^ (x | ~z)) +#define F4(x, y, z) _mm_xor_si128(y, _mm_or_si128(x, _mm_xor_si128(z, _mm_cmpeq_epi32(z, z)))) + +#define MD5STEP(f, w, x, y, z, ix, s, sc) w = _mm_add_epi32(_mm_srli_epi64(_mm_shuffle_epi32(_mm_add_epi32(_mm_add_epi32(w, _mm_set1_epi32(((unsigned __int32 *) pdata)[ix] + sc)), f(x, y, z)), _MM_SHUFFLE(0, 0, 0, 0)), 32 - s), x) +/* +#define MD5STEP(f, w, x, y, z, ix, s, sc) { \ + w = _mm_add_epi32(_mm_add_epi32(w, _mm_set1_epi32(((unsigned __int32 *) pdata)[ix] + sc)), f(x, y, z)); \ + w = _mm_shuffle_epi32(w, _MM_SHUFFLE(0, 0, 0, 0)); \ + w = _mm_srli_epi64(w, 32 - s); \ + w = _mm_add_epi32(w, x); \ +} +*/ + +// SSE2 version +void Phmd5DoBlocks( + unsigned char *hash, + char *pdata, + size_t bytecnt +) { + __m128i h0, h1, h2, h3; + __m128i a, b, c, d; + + h0 = _mm_loadu_si128((__m128i *) hash); // h0 = [a0, a1, a2, a3] (little endian) + h1 = _mm_srli_si128(h0, 4); // h1 = [a1, a2, a3, 0] + h2 = _mm_srli_si128(h0, 8); // h2 = [a2, a3, 0, 0] + h3 = _mm_srli_si128(h0, 12); // h3 = [a3, 0, 0, 0] + _mm_store_si128(&a, h0); + _mm_store_si128(&b, h1); + _mm_store_si128(&c, h2); + _mm_store_si128(&d, h3); + + do { + MD5STEP(F1, a, b, c, d, 0, 7, 0xd76aa478); + MD5STEP(F1, d, a, b, c, 1, 12, 0xe8c7b756); + MD5STEP(F1, c, d, a, b, 2, 17, 0x242070db); + MD5STEP(F1, b, c, d, a, 3, 22, 0xc1bdceee); + MD5STEP(F1, a, b, c, d, 4, 7, 0xf57c0faf); + MD5STEP(F1, d, a, b, c, 5, 12, 0x4787c62a); + MD5STEP(F1, c, d, a, b, 6, 17, 0xa8304613); + MD5STEP(F1, b, c, d, a, 7, 22, 0xfd469501); + MD5STEP(F1, a, b, c, d, 8, 7, 0x698098d8); + MD5STEP(F1, d, a, b, c, 9, 12, 0x8b44f7af); + MD5STEP(F1, c, d, a, b, 10, 17, 0xffff5bb1); + MD5STEP(F1, b, c, d, a, 11, 22, 0x895cd7be); + MD5STEP(F1, a, b, c, d, 12, 7, 0x6b901122); + MD5STEP(F1, d, a, b, c, 13, 12, 0xfd987193); + MD5STEP(F1, c, d, a, b, 14, 17, 0xa679438e); + MD5STEP(F1, b, c, d, a, 15, 22, 0x49b40821); + + MD5STEP(F2, a, b, c, d, 1, 5, 0xf61e2562); + MD5STEP(F2, d, a, b, c, 6, 9, 0xc040b340); + MD5STEP(F2, c, d, a, b, 11, 14, 0x265e5a51); + MD5STEP(F2, b, c, d, a, 0, 20, 0xe9b6c7aa); + MD5STEP(F2, a, b, c, d, 5, 5, 0xd62f105d); + MD5STEP(F2, d, a, b, c, 10, 9, 0x02441453); + MD5STEP(F2, c, d, a, b, 15, 14, 0xd8a1e681); + MD5STEP(F2, b, c, d, a, 4, 20, 0xe7d3fbc8); + MD5STEP(F2, a, b, c, d, 9, 5, 0x21e1cde6); + MD5STEP(F2, d, a, b, c, 14, 9, 0xc33707d6); + MD5STEP(F2, c, d, a, b, 3, 14, 0xf4d50d87); + MD5STEP(F2, b, c, d, a, 8, 20, 0x455a14ed); + MD5STEP(F2, a, b, c, d, 13, 5, 0xa9e3e905); + MD5STEP(F2, d, a, b, c, 2, 9, 0xfcefa3f8); + MD5STEP(F2, c, d, a, b, 7, 14, 0x676f02d9); + MD5STEP(F2, b, c, d, a, 12, 20, 0x8d2a4c8a); + + MD5STEP(F3, a, b, c, d, 5, 4, 0xfffa3942); + MD5STEP(F3, d, a, b, c, 8, 11, 0x8771f681); + MD5STEP(F3, c, d, a, b, 11, 16, 0x6d9d6122); + MD5STEP(F3, b, c, d, a, 14, 23, 0xfde5380c); + MD5STEP(F3, a, b, c, d, 1, 4, 0xa4beea44); + MD5STEP(F3, d, a, b, c, 4, 11, 0x4bdecfa9); + MD5STEP(F3, c, d, a, b, 7, 16, 0xf6bb4b60); + MD5STEP(F3, b, c, d, a, 10, 23, 0xbebfbc70); + MD5STEP(F3, a, b, c, d, 13, 4, 0x289b7ec6); + MD5STEP(F3, d, a, b, c, 0, 11, 0xeaa127fa); + MD5STEP(F3, c, d, a, b, 3, 16, 0xd4ef3085); + MD5STEP(F3, b, c, d, a, 6, 23, 0x04881d05); + MD5STEP(F3, a, b, c, d, 9, 4, 0xd9d4d039); + MD5STEP(F3, d, a, b, c, 12, 11, 0xe6db99e5); + MD5STEP(F3, c, d, a, b, 15, 16, 0x1fa27cf8); + MD5STEP(F3, b, c, d, a, 2, 23, 0xc4ac5665); + + MD5STEP(F4, a, b, c, d, 0, 6, 0xf4292244); + MD5STEP(F4, d, a, b, c, 7, 10, 0x432aff97); + MD5STEP(F4, c, d, a, b, 14, 15, 0xab9423a7); + MD5STEP(F4, b, c, d, a, 5, 21, 0xfc93a039); + MD5STEP(F4, a, b, c, d, 12, 6, 0x655b59c3); + MD5STEP(F4, d, a, b, c, 3, 10, 0x8f0ccc92); + MD5STEP(F4, c, d, a, b, 10, 15, 0xffeff47d); + MD5STEP(F4, b, c, d, a, 1, 21, 0x85845dd1); + MD5STEP(F4, a, b, c, d, 8, 6, 0x6fa87e4f); + MD5STEP(F4, d, a, b, c, 15, 10, 0xfe2ce6e0); + MD5STEP(F4, c, d, a, b, 6, 15, 0xa3014314); + MD5STEP(F4, b, c, d, a, 13, 21, 0x4e0811a1); + MD5STEP(F4, a, b, c, d, 4, 6, 0xf7537e82); + MD5STEP(F4, d, a, b, c, 11, 10, 0xbd3af235); + MD5STEP(F4, c, d, a, b, 2, 15, 0x2ad7d2bb); + MD5STEP(F4, b, c, d, a, 9, 21, 0xeb86d391); + + a = _mm_add_epi32(a, h0); + b = _mm_add_epi32(b, h1); + c = _mm_add_epi32(c, h2); + d = _mm_add_epi32(d, h3); + + _mm_store_si128(&h0, a); + _mm_store_si128(&h1, b); + _mm_store_si128(&h2, c); + _mm_store_si128(&h3, d); + + pdata += 64; + } while (bytecnt -= 64); + + *(unsigned __int32 *) &hash[ 0] = _mm_cvtsi128_si32(h0); + *(unsigned __int32 *) &hash[ 4] = _mm_cvtsi128_si32(h1); + *(unsigned __int32 *) &hash[ 8] = _mm_cvtsi128_si32(h2); + *(unsigned __int32 *) &hash[12] = _mm_cvtsi128_si32(h3); +} + + +// for update with null bytes +#define MD5STEP0(f, w, x, y, z, s, sc) w = _mm_add_epi32(_mm_srli_epi64(_mm_shuffle_epi32(_mm_add_epi32(_mm_add_epi32(w, _mm_set1_epi32(sc)), f(x, y, z)), _MM_SHUFFLE(0, 0, 0, 0)), 32 - s), x) + +// zero fill version +void Phmd5DoBlocksZero( + unsigned char *hash, + size_t bytecnt +) { + __m128i h0, h1, h2, h3; + __m128i a, b, c, d; + + h0 = _mm_loadu_si128((__m128i *) hash); // h0 = [a0, a1, a2, a3] (little endian) + h1 = _mm_srli_si128(h0, 4); // h1 = [a1, a2, a3, 0] + h2 = _mm_srli_si128(h0, 8); // h2 = [a2, a3, 0, 0] + h3 = _mm_srli_si128(h0, 12); // h3 = [a3, 0, 0, 0] + _mm_store_si128(&a, h0); + _mm_store_si128(&b, h1); + _mm_store_si128(&c, h2); + _mm_store_si128(&d, h3); + + do { + MD5STEP0(F1, a, b, c, d, 7, 0xd76aa478); + MD5STEP0(F1, d, a, b, c, 12, 0xe8c7b756); + MD5STEP0(F1, c, d, a, b, 17, 0x242070db); + MD5STEP0(F1, b, c, d, a, 22, 0xc1bdceee); + MD5STEP0(F1, a, b, c, d, 7, 0xf57c0faf); + MD5STEP0(F1, d, a, b, c, 12, 0x4787c62a); + MD5STEP0(F1, c, d, a, b, 17, 0xa8304613); + MD5STEP0(F1, b, c, d, a, 22, 0xfd469501); + MD5STEP0(F1, a, b, c, d, 7, 0x698098d8); + MD5STEP0(F1, d, a, b, c, 12, 0x8b44f7af); + MD5STEP0(F1, c, d, a, b, 17, 0xffff5bb1); + MD5STEP0(F1, b, c, d, a, 22, 0x895cd7be); + MD5STEP0(F1, a, b, c, d, 7, 0x6b901122); + MD5STEP0(F1, d, a, b, c, 12, 0xfd987193); + MD5STEP0(F1, c, d, a, b, 17, 0xa679438e); + MD5STEP0(F1, b, c, d, a, 22, 0x49b40821); + + MD5STEP0(F2, a, b, c, d, 5, 0xf61e2562); + MD5STEP0(F2, d, a, b, c, 9, 0xc040b340); + MD5STEP0(F2, c, d, a, b, 14, 0x265e5a51); + MD5STEP0(F2, b, c, d, a, 20, 0xe9b6c7aa); + MD5STEP0(F2, a, b, c, d, 5, 0xd62f105d); + MD5STEP0(F2, d, a, b, c, 9, 0x02441453); + MD5STEP0(F2, c, d, a, b, 14, 0xd8a1e681); + MD5STEP0(F2, b, c, d, a, 20, 0xe7d3fbc8); + MD5STEP0(F2, a, b, c, d, 5, 0x21e1cde6); + MD5STEP0(F2, d, a, b, c, 9, 0xc33707d6); + MD5STEP0(F2, c, d, a, b, 14, 0xf4d50d87); + MD5STEP0(F2, b, c, d, a, 20, 0x455a14ed); + MD5STEP0(F2, a, b, c, d, 5, 0xa9e3e905); + MD5STEP0(F2, d, a, b, c, 9, 0xfcefa3f8); + MD5STEP0(F2, c, d, a, b, 14, 0x676f02d9); + MD5STEP0(F2, b, c, d, a, 20, 0x8d2a4c8a); + + MD5STEP0(F3, a, b, c, d, 4, 0xfffa3942); + MD5STEP0(F3, d, a, b, c, 11, 0x8771f681); + MD5STEP0(F3, c, d, a, b, 16, 0x6d9d6122); + MD5STEP0(F3, b, c, d, a, 23, 0xfde5380c); + MD5STEP0(F3, a, b, c, d, 4, 0xa4beea44); + MD5STEP0(F3, d, a, b, c, 11, 0x4bdecfa9); + MD5STEP0(F3, c, d, a, b, 16, 0xf6bb4b60); + MD5STEP0(F3, b, c, d, a, 23, 0xbebfbc70); + MD5STEP0(F3, a, b, c, d, 4, 0x289b7ec6); + MD5STEP0(F3, d, a, b, c, 11, 0xeaa127fa); + MD5STEP0(F3, c, d, a, b, 16, 0xd4ef3085); + MD5STEP0(F3, b, c, d, a, 23, 0x04881d05); + MD5STEP0(F3, a, b, c, d, 4, 0xd9d4d039); + MD5STEP0(F3, d, a, b, c, 11, 0xe6db99e5); + MD5STEP0(F3, c, d, a, b, 16, 0x1fa27cf8); + MD5STEP0(F3, b, c, d, a, 23, 0xc4ac5665); + + MD5STEP0(F4, a, b, c, d, 6, 0xf4292244); + MD5STEP0(F4, d, a, b, c, 10, 0x432aff97); + MD5STEP0(F4, c, d, a, b, 15, 0xab9423a7); + MD5STEP0(F4, b, c, d, a, 21, 0xfc93a039); + MD5STEP0(F4, a, b, c, d, 6, 0x655b59c3); + MD5STEP0(F4, d, a, b, c, 10, 0x8f0ccc92); + MD5STEP0(F4, c, d, a, b, 15, 0xffeff47d); + MD5STEP0(F4, b, c, d, a, 21, 0x85845dd1); + MD5STEP0(F4, a, b, c, d, 6, 0x6fa87e4f); + MD5STEP0(F4, d, a, b, c, 10, 0xfe2ce6e0); + MD5STEP0(F4, c, d, a, b, 15, 0xa3014314); + MD5STEP0(F4, b, c, d, a, 21, 0x4e0811a1); + MD5STEP0(F4, a, b, c, d, 6, 0xf7537e82); + MD5STEP0(F4, d, a, b, c, 10, 0xbd3af235); + MD5STEP0(F4, c, d, a, b, 15, 0x2ad7d2bb); + MD5STEP0(F4, b, c, d, a, 21, 0xeb86d391); + + a = _mm_add_epi32(a, h0); + b = _mm_add_epi32(b, h1); + c = _mm_add_epi32(c, h2); + d = _mm_add_epi32(d, h3); + + _mm_store_si128(&h0, a); + _mm_store_si128(&h1, b); + _mm_store_si128(&h2, c); + _mm_store_si128(&h3, d); + + } while (bytecnt -= 64); + + *(unsigned __int32 *) &hash[ 0] = _mm_cvtsi128_si32(h0); + *(unsigned __int32 *) &hash[ 4] = _mm_cvtsi128_si32(h1); + *(unsigned __int32 *) &hash[ 8] = _mm_cvtsi128_si32(h2); + *(unsigned __int32 *) &hash[12] = _mm_cvtsi128_si32(h3); +} + diff --git a/source/par2j/phmd5s.c b/source/par2j/phmd5s.c new file mode 100644 index 0000000..82ac0e7 --- /dev/null +++ b/source/par2j/phmd5s.c @@ -0,0 +1,277 @@ +// SIMD function by Yutaka Sawada 2021-02-05 + +#include +#include // MMX ~ SSE2 命令セットを使用する場合インクルード +#include "phmd5.h" + + +//#define F1(x, y, z) (((y ^ z) & x) ^ z) +#define F1(x, y, z) _mm_xor_si128(_mm_and_si128(_mm_xor_si128(y, z), x), z) + +//#define F2(x, y, z) ((z & x) + (~z & y)) +#define F2(x, y, z) _mm_or_si128(_mm_and_si128(x, z), _mm_andnot_si128(z, y)) + +//#define F3(x, y, z) (x ^ y ^ z) +#define F3(x, y, z) _mm_xor_si128(x, _mm_xor_si128(y, z)) + +//#define F4(x, y, z) (y ^ (x | ~z)) +#define F4(x, y, z) _mm_xor_si128(y, _mm_or_si128(x, _mm_xor_si128(z, _mm_cmpeq_epi32(z, z)))) +//#define F4(x, y, z) _mm_xor_si128(y, _mm_or_si128(x, _mm_xor_si128(z, _mm_set1_epi32(0xffffffff)))) + + +// ビットローテーションをシャッフル命令で置き換える +#define MD5STEP(f, w, x, y, z, ix, s, sc) w = _mm_add_epi32(_mm_srli_epi64(_mm_shuffle_epi32(_mm_add_epi32(_mm_add_epi32(w, _mm_add_epi32(XX##ix, _mm_set1_epi32(sc))), f(x, y, z)), _MM_SHUFFLE(2, 2, 0, 0)), 32 - s), x) + +// 展開した場合 +/* +#define MD5STEP(f, w, x, y, z, ix, s, sc) { \ + w = _mm_add_epi32(_mm_add_epi32(w, _mm_add_epi32(XX##ix, _mm_set1_epi32(sc))), f(x, y, z)); \ + w = _mm_shuffle_epi32(w, _MM_SHUFFLE(2, 2, 0, 0)); \ + w = _mm_srli_epi64(w, 32 - s); \ + w = _mm_add_epi32(w, x); \ +} +*/ + +// Read two 32-bit integers twice +// XX##a = [a0, a1, 0, 0] read 8-bytes each (little endian) +// XX##b = [b0, b1, 0, 0] +// XX##a = [a0, b0, a1, b1] after _mm_unpacklo_epi32(XX##a, XX##b) +// XX##b = [a1, a1, b1, b1] after _mm_unpackhi_epi32(XX##a, XX##a) +// XX##a = [a0, a0, b0, b0] after _mm_unpacklo_epi32(XX##a, XX##a) +#define READ2(a, b, x) { \ + XX##a = _mm_loadl_epi64((__m128i *) (pdata + x)); \ + XX##b = _mm_loadl_epi64((__m128i *) (pdata2 + x)); \ + XX##a = _mm_unpacklo_epi32(XX##a, XX##b); \ + XX##b = _mm_unpackhi_epi32(XX##a, XX##a); \ + XX##a = _mm_unpacklo_epi32(XX##a, XX##a); \ +} + +// Read four 32-bit integers twice +// XX##a = [a0, a1, a2, a3] read 16-bytes each (little endian) +// XX##b = [b0, b1, b2, b3] +// XX##c = [a2, b2, a3, b3] after _mm_unpackhi_epi32(XX##a, XX##b) +// XX##a = [a0, b0, a1, b1] after _mm_unpacklo_epi32(XX##a, XX##b) +// XX##b = [a1, a1, b1, b1] after _mm_unpackhi_epi32(XX##a, XX##a) +// XX##a = [a0, a0, b0, b0] after _mm_unpacklo_epi32(XX##a, XX##a) +// XX##d = [a3, a3, b3, b3] after _mm_unpackhi_epi32(XX##c, XX##c) +// XX##c = [a2, a2, b2, b2] after _mm_unpacklo_epi32(XX##c, XX##c) +#define READ4(a, b, c, d, x) { \ + XX##a = _mm_loadu_si128((__m128i *) (pdata + x)); \ + XX##b = _mm_loadu_si128((__m128i *) (pdata2 + x)); \ + XX##c = _mm_unpackhi_epi32(XX##a, XX##b); \ + XX##a = _mm_unpacklo_epi32(XX##a, XX##b); \ + XX##b = _mm_unpackhi_epi32(XX##a, XX##a); \ + XX##a = _mm_unpacklo_epi32(XX##a, XX##a); \ + XX##d = _mm_unpackhi_epi32(XX##c, XX##c); \ + XX##c = _mm_unpacklo_epi32(XX##c, XX##c); \ +} + +void Phmd5DoBlocks2( + unsigned char *hash, + unsigned char *hash2, + char *pdata, + char *pdata2, + size_t bytecnt +) { + __m128i h0, h1, h2, h3; + __m128i a, b, c, d; + __m128i XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7; + __m128i XX8, XX9, XX10, XX11, XX12, XX13, XX14, XX15; + + // same method as READ4 + h0 = _mm_loadu_si128((__m128i *) hash); + h1 = _mm_loadu_si128((__m128i *) hash2); + h2 = _mm_unpackhi_epi32(h0, h1); + h0 = _mm_unpacklo_epi32(h0, h1); + h1 = _mm_unpackhi_epi32(h0, h0); + h0 = _mm_unpacklo_epi32(h0, h0); + h3 = _mm_unpackhi_epi32(h2, h2); + h2 = _mm_unpacklo_epi32(h2, h2); +// h0 = _mm_set_epi32(0, *(unsigned __int32 *) &hash2[ 0], 0, *(unsigned __int32 *) &hash[ 0] ); +// h1 = _mm_set_epi32(0, *(unsigned __int32 *) &hash2[ 4], 0, *(unsigned __int32 *) &hash[ 4] ); +// h2 = _mm_set_epi32(0, *(unsigned __int32 *) &hash2[ 8], 0, *(unsigned __int32 *) &hash[ 8] ); +// h3 = _mm_set_epi32(0, *(unsigned __int32 *) &hash2[12], 0, *(unsigned __int32 *) &hash[12] ); + _mm_store_si128(&a, h0); + _mm_store_si128(&b, h1); + _mm_store_si128(&c, h2); + _mm_store_si128(&d, h3); + + do { +// READ4( 0, 1, 2, 3, 0); + READ2( 0, 1, 0); + MD5STEP(F1, a, b, c, d, 0, 7, 0xd76aa478); + MD5STEP(F1, d, a, b, c, 1, 12, 0xe8c7b756); + READ2( 2, 3, 8); + MD5STEP(F1, c, d, a, b, 2, 17, 0x242070db); + MD5STEP(F1, b, c, d, a, 3, 22, 0xc1bdceee); +// READ4( 4, 5, 6, 7, 16); + READ2( 4, 5, 16); + MD5STEP(F1, a, b, c, d, 4, 7, 0xf57c0faf); + MD5STEP(F1, d, a, b, c, 5, 12, 0x4787c62a); + READ2( 6, 7, 24); + MD5STEP(F1, c, d, a, b, 6, 17, 0xa8304613); + MD5STEP(F1, b, c, d, a, 7, 22, 0xfd469501); +// READ4( 8, 9, 10, 11, 32); + READ2( 8, 9, 32); + MD5STEP(F1, a, b, c, d, 8, 7, 0x698098d8); + MD5STEP(F1, d, a, b, c, 9, 12, 0x8b44f7af); + READ2(10, 11, 40); + MD5STEP(F1, c, d, a, b, 10, 17, 0xffff5bb1); + MD5STEP(F1, b, c, d, a, 11, 22, 0x895cd7be); +// READ4(12, 13, 14, 15, 48); + READ2(12, 13, 48); + MD5STEP(F1, a, b, c, d, 12, 7, 0x6b901122); + MD5STEP(F1, d, a, b, c, 13, 12, 0xfd987193); + READ2(14, 15, 56); + MD5STEP(F1, c, d, a, b, 14, 17, 0xa679438e); + MD5STEP(F1, b, c, d, a, 15, 22, 0x49b40821); + + MD5STEP(F2, a, b, c, d, 1, 5, 0xf61e2562); + MD5STEP(F2, d, a, b, c, 6, 9, 0xc040b340); + MD5STEP(F2, c, d, a, b, 11, 14, 0x265e5a51); + MD5STEP(F2, b, c, d, a, 0, 20, 0xe9b6c7aa); + MD5STEP(F2, a, b, c, d, 5, 5, 0xd62f105d); + MD5STEP(F2, d, a, b, c, 10, 9, 0x02441453); + MD5STEP(F2, c, d, a, b, 15, 14, 0xd8a1e681); + MD5STEP(F2, b, c, d, a, 4, 20, 0xe7d3fbc8); + MD5STEP(F2, a, b, c, d, 9, 5, 0x21e1cde6); + MD5STEP(F2, d, a, b, c, 14, 9, 0xc33707d6); + MD5STEP(F2, c, d, a, b, 3, 14, 0xf4d50d87); + MD5STEP(F2, b, c, d, a, 8, 20, 0x455a14ed); + MD5STEP(F2, a, b, c, d, 13, 5, 0xa9e3e905); + MD5STEP(F2, d, a, b, c, 2, 9, 0xfcefa3f8); + MD5STEP(F2, c, d, a, b, 7, 14, 0x676f02d9); + MD5STEP(F2, b, c, d, a, 12, 20, 0x8d2a4c8a); + + MD5STEP(F3, a, b, c, d, 5, 4, 0xfffa3942); + MD5STEP(F3, d, a, b, c, 8, 11, 0x8771f681); + MD5STEP(F3, c, d, a, b, 11, 16, 0x6d9d6122); + MD5STEP(F3, b, c, d, a, 14, 23, 0xfde5380c); + MD5STEP(F3, a, b, c, d, 1, 4, 0xa4beea44); + MD5STEP(F3, d, a, b, c, 4, 11, 0x4bdecfa9); + MD5STEP(F3, c, d, a, b, 7, 16, 0xf6bb4b60); + MD5STEP(F3, b, c, d, a, 10, 23, 0xbebfbc70); + MD5STEP(F3, a, b, c, d, 13, 4, 0x289b7ec6); + MD5STEP(F3, d, a, b, c, 0, 11, 0xeaa127fa); + MD5STEP(F3, c, d, a, b, 3, 16, 0xd4ef3085); + MD5STEP(F3, b, c, d, a, 6, 23, 0x04881d05); + MD5STEP(F3, a, b, c, d, 9, 4, 0xd9d4d039); + MD5STEP(F3, d, a, b, c, 12, 11, 0xe6db99e5); + MD5STEP(F3, c, d, a, b, 15, 16, 0x1fa27cf8); + MD5STEP(F3, b, c, d, a, 2, 23, 0xc4ac5665); + + MD5STEP(F4, a, b, c, d, 0, 6, 0xf4292244); + MD5STEP(F4, d, a, b, c, 7, 10, 0x432aff97); + MD5STEP(F4, c, d, a, b, 14, 15, 0xab9423a7); + MD5STEP(F4, b, c, d, a, 5, 21, 0xfc93a039); + MD5STEP(F4, a, b, c, d, 12, 6, 0x655b59c3); + MD5STEP(F4, d, a, b, c, 3, 10, 0x8f0ccc92); + MD5STEP(F4, c, d, a, b, 10, 15, 0xffeff47d); + MD5STEP(F4, b, c, d, a, 1, 21, 0x85845dd1); + MD5STEP(F4, a, b, c, d, 8, 6, 0x6fa87e4f); + MD5STEP(F4, d, a, b, c, 15, 10, 0xfe2ce6e0); + MD5STEP(F4, c, d, a, b, 6, 15, 0xa3014314); + MD5STEP(F4, b, c, d, a, 13, 21, 0x4e0811a1); + MD5STEP(F4, a, b, c, d, 4, 6, 0xf7537e82); + MD5STEP(F4, d, a, b, c, 11, 10, 0xbd3af235); + MD5STEP(F4, c, d, a, b, 2, 15, 0x2ad7d2bb); + MD5STEP(F4, b, c, d, a, 9, 21, 0xeb86d391); + + a = _mm_add_epi32(a, h0); + b = _mm_add_epi32(b, h1); + c = _mm_add_epi32(c, h2); + d = _mm_add_epi32(d, h3); + + _mm_store_si128(&h0, a); + _mm_store_si128(&h1, b); + _mm_store_si128(&h2, c); + _mm_store_si128(&h3, d); + + pdata += 64; + pdata2 += 64; + } while (bytecnt -= 64); + + *(unsigned __int32 *) &hash[ 0] = _mm_cvtsi128_si32(h0); + *(unsigned __int32 *) &hash[ 4] = _mm_cvtsi128_si32(h1); + *(unsigned __int32 *) &hash[ 8] = _mm_cvtsi128_si32(h2); + *(unsigned __int32 *) &hash[12] = _mm_cvtsi128_si32(h3); + h0 = _mm_srli_si128(h0, 8); // right shift 8-bytes + h1 = _mm_srli_si128(h1, 8); + h2 = _mm_srli_si128(h2, 8); + h3 = _mm_srli_si128(h3, 8); + *(unsigned __int32 *) &hash2[ 0] = _mm_cvtsi128_si32(h0); + *(unsigned __int32 *) &hash2[ 4] = _mm_cvtsi128_si32(h1); + *(unsigned __int32 *) &hash2[ 8] = _mm_cvtsi128_si32(h2); + *(unsigned __int32 *) &hash2[12] = _mm_cvtsi128_si32(h3); +} + +// SIMD version updates two MD5 at once. +// The data must be dword (4-bytes) aligned. +void Phmd5Process2(PHMD5 *pmd5, PHMD5 *pmd52, char *pdata, size_t bytecnt) { + char *pdata2; + size_t bytefin, bytecnt2; + unsigned cb, resid, resid2; + + pdata2 = pdata; + bytecnt2 = bytecnt; + resid = (unsigned) pmd5->totbyt; + resid2 = (unsigned) pmd52->totbyt; + pmd5->totbyt += bytecnt; // update total bytes processed + pmd52->totbyt += bytecnt; + + resid &= 63; // count of bytes now in pmd5->buf + resid2 &= 63; + + // This block handles the case of residual data in pmd5->buf. + // After this block pmd5->buf is empty (except perhaps on exit). + + if (resid) { // if residual exists, + cb = 64 - resid; + if (cb > bytecnt) cb = (unsigned) bytecnt; + memcpy(pmd5->buf + resid, pdata, cb); + pdata += cb; + bytecnt -= cb; + if (resid + cb == 64) Phmd5DoBlocks(pmd5->hash, pmd5->buf, 64); + } + bytefin = bytecnt & ~63; + if (resid2) { + cb = 64 - resid2; + if (cb > bytecnt2) cb = (unsigned) bytecnt2; + memcpy(pmd52->buf + resid2, pdata2, cb); + pdata2 += cb; + bytecnt2 -= cb; + if (bytecnt2 < bytefin) bytefin = bytecnt2 & ~63; // shorter size + if (resid2 + cb == 64) Phmd5DoBlocks(pmd52->hash, pmd52->buf, 64); + } + + // This block processes input data in-place, if the data is dword + // aligned and in 64-byte chunks. + + if (bytefin) { + //Phmd5DoBlocks(pmd5->hash, pdata, bytefin); + //Phmd5DoBlocks(pmd52->hash, pdata2, bytefin); + Phmd5DoBlocks2(pmd5->hash, pmd52->hash, pdata, pdata2, bytefin); + pdata += bytefin; + pdata2 += bytefin; + bytecnt -= bytefin; + bytecnt2 -= bytefin; + } + + while (bytecnt) { // handle residual/non-aligned data + cb = 64 > (unsigned) bytecnt ? (unsigned) bytecnt : 64; + memcpy(pmd5->buf, pdata, cb); + pdata += cb; + bytecnt -= cb; + if (cb < 64) break; + Phmd5DoBlocks(pmd5->hash, pmd5->buf, 64); + }; + while (bytecnt2) { + cb = 64 > (unsigned) bytecnt2 ? (unsigned) bytecnt2 : 64; + memcpy(pmd52->buf, pdata2, cb); + pdata2 += cb; + bytecnt2 -= cb; + if (cb < 64) break; + Phmd5DoBlocks(pmd52->hash, pmd52->buf, 64); + }; +} + diff --git a/source/par2j/reedsolomon.c b/source/par2j/reedsolomon.c new file mode 100644 index 0000000..63225ce --- /dev/null +++ b/source/par2j/reedsolomon.c @@ -0,0 +1,816 @@ +// reedsolomon.c +// Copyright : 2022-10-08 Yutaka Sawada +// License : GPL + +#ifndef _UNICODE +#define _UNICODE +#endif +#ifndef UNICODE +#define UNICODE +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 // Windows Vista or later +#endif + +#include +#include +#include + +#include + +#include "common2.h" +#include "crc.h" +#include "gf16.h" +#include "phmd5.h" +#include "lib_opencl.h" +#include "rs_encode.h" +#include "rs_decode.h" +#include "reedsolomon.h" + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// chunk がキャッシュに収まるようにすれば速くなる! (Cache Blocking という最適化手法) +int try_cache_blocking(int unit_size) +{ + int limit_size, chunk_count, chunk_size, cache_line_diff; + + // CPUキャッシュをどのくらいまで使うか + limit_size = cpu_flag & 0x7FFF8000; // 最低でも 32KB になる + if (limit_size == 0) // キャッシュ・サイズを取得できなかった場合は最適化しない + return unit_size; + + // キャッシュにうまく収まるように chunk のサイズを決める + cache_line_diff = 64 - sse_unit; // cache line size とデータ境界の差 + if (cache_line_diff < 0) + cache_line_diff = 0; + chunk_count = 1; + chunk_size = unit_size; // unit_size は sse_unit の倍数になってる + while (chunk_size + cache_line_diff > limit_size){ // 制限サイズより大きいなら + // 分割数を増やして chunk のサイズを試算してみる + chunk_count++; + chunk_size = (unit_size + chunk_count - 1) / chunk_count; + chunk_size = (chunk_size + (sse_unit - 1)) & ~(sse_unit - 1); // sse_unit の倍数にする + } + + return chunk_size; +} + +// 空きメモリー量からファイル・アクセスのバッファー・サイズを計算する +// io_size = unit_size - HASH_SIZE になることに注意 (alloc_unit >= HASH_SIZE) +unsigned int get_io_size( + unsigned int buf_num, // 何ブロック分の領域を確保するのか + unsigned int *part_num, // 部分的なエンコード用の作業領域 + size_t trial_alloc, // 確保できるか確認するのか + int alloc_unit) // メモリー単位の境界 (sse_unit か MEM_UNIT) +{ + unsigned int unit_size, io_size, part_max, part_min; + size_t mem_size, io_size64; + + if (part_num == NULL){ // 指定が無ければ調節しない + part_max = 0; + part_min = 0; + } else { + part_max = *part_num; // 初期値には最大値をセットする + part_min = source_num >> PART_MIN_RATE; + part_min = (part_min / cpu_num) * cpu_num; // cpu_num の倍数にする(切り下げ) + if ((int)part_min < cpu_num * 2) + part_min = cpu_num * 2; // ダブル・バッファリングするなら cpu_num の倍以上にすること + if (part_min > part_max) + part_min = part_max; +#ifdef TIMER + printf("get_io_size: part_min = %d, part_max = %d\n", part_min, part_max); +#endif + } + // alloc_unit の倍数にする + unit_size = (block_size + HASH_SIZE + (alloc_unit - 1)) & ~(alloc_unit - 1); + + if (trial_alloc){ + __int64 possible_size; + possible_size = (__int64)unit_size * (buf_num + part_max); +#ifndef _WIN64 // 32-bit 版なら + if (possible_size > MAX_MEM_SIZE) // 確保する最大サイズを 2GB までにする + possible_size = MAX_MEM_SIZE; + if (check_OS64() == 0){ // 32-bit OS 上なら更に制限する + if (possible_size > MAX_MEM_SIZE32) + possible_size = MAX_MEM_SIZE32; + } +#endif + trial_alloc = (size_t)possible_size; + trial_alloc = (trial_alloc + 0xFFFF) & ~0xFFFF; // 64KB の倍数にしておく + } + mem_size = get_mem_size(trial_alloc); + io_size64 = mem_size / (buf_num + part_max) - HASH_SIZE; // 何個分必要か + + // ブロック・サイズより大きい、またはブロック・サイズ自体が小さい場合は + if ((io_size64 >= (size_t)block_size) || (block_size <= 1024)){ + io_size = unit_size - HASH_SIZE; // ブロック・サイズ - HASH_SIZE + + } else { // ブロック・サイズを等分割する + unsigned int num, num2; + io_size = (unsigned int)io_size64; + num = (block_size + io_size - 1) / io_size; // ブロックを何分割するか + if (part_min < part_max){ // 保持する量に幅があるなら + io_size64 = mem_size / (buf_num + part_min) - HASH_SIZE; // 確保するサイズを最低限にした場合 + if (io_size64 >= (size_t)block_size){ + num2 = 1; + } else { + io_size = (unsigned int)io_size64; + num2 = (block_size + io_size - 1) / io_size; + } + } else { + num2 = num; + } + if (num > num2){ // 確保量を減らしたほうがブロックの分割数が減るなら + io_size = (block_size + num2 - 1) / num2; + if (io_size < 1024) + io_size = 1024; + num = (unsigned int)(mem_size / (io_size + HASH_SIZE)) - buf_num; + if (num < part_max){ // 分割して計算するなら + num2 = (parity_num + num - 1) / num; // 分割回数 + num = (parity_num + num2 - 1) / num2; + num = ((num + cpu_num - 1) / cpu_num) * cpu_num; // cpu_num の倍数にする(切り上げ) + if (num < part_min) + num = part_min; + } + if (num > part_max) + num = part_max; + *part_num = num; + } else { + io_size = (block_size + num - 1) / num; + if (io_size < 1024) + io_size = 1024; // 断片化する場合でもブロック数が多いと 32768 KB は使う + } + io_size = ((io_size + HASH_SIZE + (alloc_unit - 1)) & ~(alloc_unit - 1)) - HASH_SIZE; // alloc_unit の倍数 - HASH_SIZE + } + + return io_size; +} + +// 何ブロックまとめてファイルから読み込むかを空きメモリー量から計算する +int read_block_num( + int keep_num, // 保持するパリティ・ブロック数 + int add_num, // 余裕を見るブロック数 + size_t trial_alloc, // 確保できるか確認するのか + int alloc_unit) // メモリー単位の境界 (sse_unit か MEM_UNIT) +{ + int buf_num, read_min; + unsigned int unit_size; + size_t mem_size; + + read_min = keep_num >> READ_MIN_RATE; + if (read_min < READ_MIN_NUM) + read_min = READ_MIN_NUM; + if (read_min > source_num) + read_min = source_num; + unit_size = (block_size + HASH_SIZE + (alloc_unit - 1)) & ~(alloc_unit - 1); + + if (trial_alloc){ + __int64 possible_size; + possible_size = (__int64)unit_size * (source_num + keep_num + add_num); +#ifndef _WIN64 // 32-bit 版なら + if (possible_size > MAX_MEM_SIZE) // 確保する最大サイズを 2GB までにする + possible_size = MAX_MEM_SIZE; + if (check_OS64() == 0){ // 32-bit OS 上なら更に制限する + if (possible_size > MAX_MEM_SIZE32) + possible_size = MAX_MEM_SIZE32; + } +#endif + trial_alloc = (size_t)possible_size; + trial_alloc = (trial_alloc + 0xFFFF) & ~0xFFFF; // 64KB の倍数にしておく + } + mem_size = get_mem_size(trial_alloc) / unit_size; // 何個分確保できるか + + if (mem_size >= (size_t)(source_num + keep_num + add_num)){ // 最大個数より多い + buf_num = source_num; + } else if ((int)mem_size < read_min + keep_num + add_num){ // 少なすぎる + buf_num = 0; // メモリー不足の印 + } else { // ソース・ブロック個数を等分割する + int split_num; + buf_num = (int)mem_size - (keep_num + add_num); + split_num = (source_num + buf_num - 1) / buf_num; // 何回に別けて読み込むか + buf_num = (source_num + split_num - 1) / split_num; + } + + return buf_num; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// 戸川 隼人 の「演習と応用FORTRAN77」の逆行列の計算方法を参考にして +// Gaussian Elimination を少し修正して行列の数を一つにしてみた + +// 半分のメモリーで逆行列を計算する (利用するパリティ・ブロックの所だけ) +static int invert_matrix_st(unsigned short *mat, + int rows, // 横行の数、行列の縦サイズ、失われたソース・ブロックの数 = 利用するパリティ・ブロック数 + int cols, // 縦列の数、行列の横サイズ、本来のソース・ブロック数 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + int i, j, row_start, row_start2, pivot, factor; + unsigned int time_last = GetTickCount(); + + // Gaussian Elimination with 1 matrix + pivot = 0; + row_start = 0; // その行の開始位置 + for (i = 0; i < rows; i++){ + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((i * 1000) / rows)) + return 2; + time_last = GetTickCount(); + } + + // その行 (パリティ・ブロック) がどのソース・ブロックの代用か + while ((pivot < cols) && (s_blk[pivot].exist != 0)) + pivot++; + + // Divide the row by element i,pivot + factor = mat[row_start + pivot]; // mat(j, pivot) は 0以外のはず + //printf("\nparity[ %u ] -> source[ %u ], factor = %u\n", id[col_find], col_find, factor); + if (factor > 1){ // factor が 1より大きいなら、1にする為に factor で割る + mat[row_start + pivot] = 1; // これが行列を一個で済ます手 + galois_region_divide(mat + row_start, cols, factor); + } else if (factor == 0){ // factor = 0 だと、その行列の逆行列を計算できない + return (0x00010000 | pivot); // どのソース・ブロックで問題が発生したのかを返す + } + + // 別の行の同じ pivot 列が 0以外なら、その値を 0にするために、 + // i 行を何倍かしたものを XOR する + for (j = rows - 1; j >= 0; j--){ + if (j == i) + continue; // 同じ行はとばす + row_start2 = cols * j; // その行の開始位置 + factor = mat[row_start2 + pivot]; // j 行の pivot 列の値 + mat[row_start2 + pivot] = 0; // これが行列を一個で済ます手 + // 先の計算により、i 行の pivot 列の値は必ず 1なので、この factor が倍率になる + galois_region_multiply(mat + row_start, mat + row_start2, cols, factor); + } + row_start += cols; // 次の行にずらす + pivot++; + } + + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// マルチ・プロセッサー対応 + +typedef struct { // RS threading control struct + unsigned short *mat; // 行列 + int cols; // 横行の長さ + volatile int start; // 掛ける行の先頭位置 + volatile int pivot; // 倍率となる値の位置 + volatile int skip; // とばす行 + volatile int now; // 消去する行 + HANDLE h; + HANDLE run; + HANDLE end; +} INV_TH; + +// サブ・スレッド +static DWORD WINAPI thread_func(LPVOID lpParameter) +{ + unsigned short *mat; + int j, cols, row_start2, factor; + INV_TH *th; + + th = (INV_TH *)lpParameter; + mat = th->mat; + cols = th->cols; + + WaitForSingleObject(th->run, INFINITE); // 計算開始の合図を待つ + while (th->skip >= 0){ + while ((j = InterlockedDecrement(&(th->now))) >= 0){ // j = --th_now + if (j == th->skip) + continue; + row_start2 = cols * j; // その行の開始位置 + factor = mat[row_start2 + th->pivot]; // j 行の pivot 列の値 + mat[row_start2 + th->pivot] = 0; // これが行列を一個で済ます手 + // 先の計算により、i 行の pivot 列の値は必ず 1なので、この factor が倍率になる + galois_region_multiply(mat + th->start, mat + row_start2, cols, factor); + } + //_mm_sfence(); // メモリーへの書き込みを完了する + SetEvent(th->end); // 計算終了を通知する + WaitForSingleObject(th->run, INFINITE); // 計算開始の合図を待つ + } + + // 終了処理 + CloseHandle(th->run); + CloseHandle(th->end); + return 0; +} + +// マルチ・スレッドで逆行列を計算する (利用するパリティ・ブロックの所だけ) +static int invert_matrix_mt(unsigned short *mat, + int rows, // 横行の数、行列の縦サイズ、失われたソース・ブロックの数 = 利用するパリティ・ブロック数 + int cols, // 縦列の数、行列の横サイズ、本来のソース・ブロック数 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + int j, row_start2, factor; + unsigned int time_last = GetTickCount(); + INV_TH th[1]; + + memset(th, 0, sizeof(INV_TH)); + + // イベントを作成する + th->run = CreateEvent(NULL, FALSE, FALSE, NULL); // 両方とも Auto Reset にする + if (th->run == NULL){ + print_win32_err(); + printf("error, inv-thread\n"); + return 1; + } + th->end = CreateEvent(NULL, FALSE, FALSE, NULL); + if (th->end == NULL){ + print_win32_err(); + CloseHandle(th->run); + printf("error, inv-thread\n"); + return 1; + } + // サブ・スレッドを起動する + th->mat = mat; + th->cols = cols; + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する + th->h = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_func, (LPVOID)th, 0, NULL); + if (th->h == NULL){ + print_win32_err(); + CloseHandle(th->run); + CloseHandle(th->end); + printf("error, inv-thread\n"); + return 1; + } + + // Gaussian Elimination with 1 matrix + th->pivot = 0; + th->start = 0; // その行の開始位置 + for (th->skip = 0; th->skip < rows; th->skip++){ + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((th->skip * 1000) / rows)){ + th->skip = -1; // 終了指示 + //_mm_sfence(); + SetEvent(th->run); + WaitForSingleObject(th->h, INFINITE); + CloseHandle(th->h); + return 2; + } + time_last = GetTickCount(); + } + + // その行 (パリティ・ブロック) がどのソース・ブロックの代用か + while ((th->pivot < cols) && (s_blk[th->pivot].exist != 0)) + th->pivot++; + + // Divide the row by element i,pivot + factor = mat[th->start + th->pivot]; + if (factor > 1){ + mat[th->start + th->pivot] = 1; // これが行列を一個で済ます手 + galois_region_divide(mat + th->start, cols, factor); + } else if (factor == 0){ // factor = 0 だと、その行列の逆行列を計算できない + th->skip = -1; // 終了指示 + //_mm_sfence(); + SetEvent(th->run); + WaitForSingleObject(th->h, INFINITE); + CloseHandle(th->h); + return (0x00010000 | th->pivot); // どのソース・ブロックで問題が発生したのかを返す + } + + // 別の行の同じ pivot 列が 0以外なら、その値を 0にするために、 + // i 行を何倍かしたものを XOR する + th->now = rows; // 初期値 + 1 + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを再開する + SetEvent(th->run); // サブ・スレッドに計算を開始させる + while ((j = InterlockedDecrement(&(th->now))) >= 0){ // j = --th_now + if (j == th->skip) // 同じ行はとばす + continue; + row_start2 = cols * j; // その行の開始位置 + factor = mat[row_start2 + th->pivot]; // j 行の pivot 列の値 + mat[row_start2 + th->pivot] = 0; // これが行列を一個で済ます手 + // 先の計算により、i 行の pivot 列の値は必ず 1なので、この factor が倍率になる + galois_region_multiply(mat + th->start, mat + row_start2, cols, factor); + } + + WaitForSingleObject(th->end, INFINITE); // サブ・スレッドの計算終了の合図を待つ + th->start += cols; + th->pivot++; + } + + // サブ・スレッドを終了させる + th->skip = -1; // 終了指示 + //_mm_sfence(); + SetEvent(th->run); + WaitForSingleObject(th->h, INFINITE); + CloseHandle(th->h); + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* +gflib の行列作成用関数や行列の逆変換用の関数を元にして、 +計算のやり方を PAR 2.0 用に修正する。 + +par-v1.1.tar.gz に含まれる rs.doc +Dummies guide to Reed-Solomon coding. を参考にする +*/ + +/* +5 * 5 なら + 1 1 1 1 1 constant の 0乗 + 2 4 16 128 256 <- この行の値を constant とする + 4 16 256 16384 4107 constant の 2乗 + 8 64 4096 8566 7099 constant の 3乗 +16 256 4107 43963 7166 constant の 4乗 + +par2-specifications.pdf によると、constant は 2の乗数で、 +その指数は (n%3 != 0 && n%5 != 0 && n%17 != 0 && n%257 != 0) になる。 +*/ + +// PAR 2.0 のパリティ検査行列はエンコード中にその場で生成する +// constant と facter の 2個のベクトルで表現する +// パリティ・ブロックごとに facter *= constant で更新していく +static void make_encode_constant( + unsigned short *constant) // constant を収めた配列 +{ + unsigned short temp; + int n, i; + + // constant は 2の乗数で、係数が3,5,17,257の倍数になるものは除く + // 定数 2, 4, 16, 128, 256, 2048, 8192, ... + n = 0; + temp = 1; + for (i = 0; i < source_num; i++){ + while (n <= 65535){ + temp = galois_multiply_fix(temp, 1); // galois_multiply(temp, 2); + n++; + if ((n % 3 != 0) && (n % 5 != 0) && (n % 17 != 0) && (n % 257 != 0)) + break; + } + constant[i] = temp; + } +} + +// 復元用の行列を作る、十分な数のパリティ・ブロックが必要 +static int make_decode_matrix( + unsigned short *mat, // 復元用の行列 + int block_lost, // 横行、行列の縦サイズ、失われたソース・ブロックの数 = 必要なパリティ・ブロック数 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + parity_ctx_r *p_blk) // 各パリティ・ブロックの情報 +{ + unsigned short *id; // 失われたソース・ブロックをどのパリティ・ブロックで代用したか + unsigned short constant; + int i, j, k, n; + + // printf("\n parity_num = %d, rows = %d, cols = %d \n", parity_num, block_lost, source_num); + // 失われたソース・ブロックをどのパリティ・ブロックで代用するか + id = mat + (block_lost * source_num); + j = 0; + for (i = 0; (i < parity_num) && (j < block_lost); i++){ + if (p_blk[i].exist == 1) // 利用不可の印が付いてるブロックは無視する + id[j++] = (unsigned short)i; + } + if (j < block_lost){ // パリティ・ブロックの数が足りなければ + printf("need more recovery slice\n"); + return 1; + } + + // 存在して利用するパリティ・ブロックだけの行列を作る + n = 0; + constant = 1; + for (i = 0; i < source_num; i++){ // 一列ずつ縦に値をセットしていく + while (n <= 65535){ + constant = galois_multiply_fix(constant, 1); // galois_multiply(constant, 2); + n++; + if ((n % 3 != 0) && (n % 5 != 0) && (n % 17 != 0) && (n % 257 != 0)) + break; + } +// printf("\n[%5d], 2 pow %5d = %5d", i, n, constant); + + k = 0; + for (j = 0; j < source_num; j++){ // j 行の i 列 + if (s_blk[j].exist == 0){ // 該当部分はパリティ・ブロックで補うのなら + mat[source_num * k + i] = galois_power(constant, id[k]); + k++; + } + } + } + + if ((cpu_num == 1) || (source_num < 10) || (block_lost < 4)){ // 小さすぎる行列はマルチ・スレッドにしない + k = invert_matrix_st(mat, block_lost, source_num, s_blk); + } else { + k = invert_matrix_mt(mat, block_lost, source_num, s_blk); + } + return k; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// リード・ソロモン符号を使ってエンコードする +int rs_encode( + wchar_t *file_path, + unsigned char *header_buf, // Recovery Slice packet のパケット・ヘッダー + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_c *files, // ソース・ファイルの情報 + source_ctx_c *s_blk, // ソース・ブロックの情報 + parity_ctx_c *p_blk) // パリティ・ブロックの情報 +{ + unsigned short *constant = NULL; + int err = 0; + unsigned int len; +#ifdef TIMER +unsigned int time_total = GetTickCount(); +#endif + + if (galois_create_table()){ + printf("galois_create_table\n"); + return 1; + } + + if (source_num == 1){ // ソース・ブロックが一個だけなら + err = encode_method1(file_path, header_buf, rcv_hFile, files, s_blk, p_blk); + goto error_end; + } + + // パリティ計算用の行列演算の準備をする + if (parity_num > source_num){ + len = sizeof(unsigned short) * (source_num + parity_num); + } else { + len = sizeof(unsigned short) * source_num * 2; + } + constant = malloc(len); + if (constant == NULL){ + printf("malloc, %d\n", len); + err = 1; + goto error_end; + } +#ifdef TIMER + printf("\nmatrix size = %d.%d KB\n", len >> 10, (len >> 10) % 10); +#endif + // パリティ検査行列の基になる定数 + make_encode_constant(constant); +// for (len = 0; (int)len < source_num; len++) +// printf("constant[%5d] = %5d\n", len, constant[len]); + +#ifdef TIMER + err = 0; // IO method : 0=Auto, -2=Read all, -4=GPU read all + if (err == 0){ +#endif + // HDD なら 1-pass & Read some 方式を使う + // メモリー不足や SSD なら、Read all 方式でブロックを断片化させる + if ((OpenCL_method != 0) && (block_size >= 65536) && (source_num >= 256) && (parity_num >= 32) && + ((source_num + parity_num) * (__int64)block_size > 1048576 * 512)){ + // ブロック数が多いなら、ブロックごとにスレッドを割り当てる (GPU を使う) + err = -4; // 2-pass & GPU read all + } else { + err = -2; // 2-pass & Read all + } +#ifdef TIMER + } +#endif + + // 最初は GPUを使い、無理なら次に移る + if (err == -4) + err = encode_method4(file_path, header_buf, rcv_hFile, files, s_blk, p_blk, constant); + if (err == -2) // ソース・データを全て読み込む場合 + err = encode_method2(file_path, header_buf, rcv_hFile, files, s_blk, p_blk, constant); +#ifdef TIMER + if (err != 1){ + time_total = GetTickCount() - time_total; + printf("total %d.%03d sec\n", time_total / 1000, time_total % 1000); + } +#endif + +error_end: + if (constant) + free(constant); + galois_free_table(); // Galois Field のテーブルを解放する + return err; +} + +// パリティ・ブロックをメモリー上に保持して、一度に読み書きする +int rs_encode_1pass( + wchar_t *file_path, + wchar_t *recovery_path, // 作業用 + int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限 + int block_distri, // パリティ・ブロックの分配方法 (3-bit目は番号の付け方) + int packet_num, // 共通パケットの数 + unsigned char *common_buf, // 共通パケットのバッファー + int common_size, // 共通パケットのバッファー・サイズ + unsigned char *footer_buf, // 末尾パケットのバッファー + int footer_size, // 末尾パケットのバッファー・サイズ + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_c *files, // ソース・ファイルの情報 + source_ctx_c *s_blk) // ソース・ブロックの情報 +{ + unsigned short *constant = NULL; + int err = 0; + unsigned int len; +#ifdef TIMER +unsigned int time_total = GetTickCount(); +#endif + + if (galois_create_table()){ + printf("galois_create_table\n"); + return 1; + } + + // パリティ計算用の行列演算の準備をする + if (parity_num > source_num){ + len = sizeof(unsigned short) * (source_num + parity_num); + } else { + len = sizeof(unsigned short) * source_num * 2; + } + constant = malloc(len); + if (constant == NULL){ + printf("malloc, %d\n", len); + err = 1; + goto error_end; + } +#ifdef TIMER + printf("\nmatrix size = %d.%d KB\n", len >> 10, (len >> 10) % 10); +#endif + // パリティ検査行列の基になる定数 + make_encode_constant(constant); +// for (len = 0; (int)len < source_num; len++) +// printf("constant[%5d] = %5d\n", len, constant[len]); + +#ifdef TIMER + err = 0; // IO method : 0=Auto, -3=Read some, -5=GPU read some, -? = Goto 2pass + if (err == 0){ +#endif + // メモリーが足りてる場合だけ 1-pass方式を使う + if ((OpenCL_method != 0) && (block_size >= 65536) && (source_num >= 256) && (parity_num >= 32) && + ((source_num + parity_num) * (__int64)block_size > 1048576 * 512)){ + err = -5; // 1-pass & GPU read some + } else { + err = -3; // 1-pass & Read some + } +#ifdef TIMER + } +#endif + + // 最初は GPUを使い、無理なら次に移る + if (err == -5) + err = encode_method5(file_path, recovery_path, packet_limit, block_distri, packet_num, + common_buf, common_size, footer_buf, footer_size, rcv_hFile, files, s_blk, constant); + if (err == -3) // ソース・データをいくつか読み込む場合 + err = encode_method3(file_path, recovery_path, packet_limit, block_distri, packet_num, + common_buf, common_size, footer_buf, footer_size, rcv_hFile, files, s_blk, constant); + +#ifdef TIMER + if (err < 0){ + printf("switching to 2-pass processing, %d\n", err); + } else if (err != 1){ + time_total = GetTickCount() - time_total; + printf("total %d.%03d sec\n", time_total / 1000, time_total % 1000); + } +#endif + +error_end: + if (constant) + free(constant); + galois_free_table(); // Galois Field のテーブルを解放する + return err; +} + +// リード・ソロモン符号を使ってデコードする +int rs_decode( + wchar_t *file_path, + int block_lost, // 失われたソース・ブロックの数 + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_r *files, // ソース・ファイルの情報 + source_ctx_r *s_blk, // ソース・ブロックの情報 + parity_ctx_r *p_blk) // パリティ・ブロックの情報 +{ + unsigned short *mat = NULL, *id; + int err = 0, i, j, k; + unsigned int len; +#ifdef TIMER +unsigned int time_matrix = 0, time_total = GetTickCount(); +#endif + + if (galois_create_table()){ + printf("galois_create_table\n"); + return 1; + } + + if (source_num == 1){ // ソース・ブロックが一個だけなら + err = decode_method1(file_path, rcv_hFile, files, s_blk, p_blk); + goto error_end; + } + + // 復元用の行列演算の準備をする + len = sizeof(unsigned short) * block_lost * (source_num + 1); + mat = malloc(len); + if (mat == NULL){ + printf("malloc, %d\n", len); + printf("matrix for recovery is too large\n"); + err = 1; + goto error_end; + } +#ifdef TIMER + if (len & 0xFFF00000){ + printf("\nmatrix size = %d.%d MB\n", len >> 20, (len >> 20) % 10); + } else { + printf("\nmatrix size = %d.%d KB\n", len >> 10, (len >> 10) % 10); + } +#endif + // 何番目の消失ソース・ブロックがどのパリティで代替されるか + id = mat + (block_lost * source_num); + +#ifdef TIMER +time_matrix = GetTickCount(); +#endif + // 復元用の行列を計算する + print_progress_text(0, "Computing matrix"); + err = make_decode_matrix(mat, block_lost, s_blk, p_blk); + while (err >= 0x00010000){ // 逆行列を計算できなかった場合 ( Petr Matas の修正案を参考に実装) + printf("\n"); + err ^= 0x00010000; // エラーが起きた行 (ソース・ブロックの番号) + printf("fail at input slice %d\n", err); + k = 0; + for (i = 0; i < err; i++){ + if (s_blk[i].exist == 0) + k++; + } + // id[k] エラーが起きた行に対応するパリティ・ブロックの番号 + p_blk[id[k]].exist = 0x100; // そのパリティ・ブロックを使わないようにする + printf("disable recovery slice %d\n", id[k]); + j = 0; + for (i = 0; i < parity_num; i++){ + if (p_blk[i].exist == 1) + j++; // 利用可能なパリティ・ブロックの数 + } + if (j >= block_lost){ // 使えるパリティ・ブロックの数が破損ブロックの数以上なら + print_progress_text(0, "Computing matrix"); + err = make_decode_matrix(mat, block_lost, s_blk, p_blk); + } else { // 代替するパリティ・ブロックの数が足りなければ + printf("fail at recovery slice"); + for (i = 0; i < parity_num; i++){ + if (p_blk[i].exist == 0x100) + printf(" %d", i); + } + printf("\n"); + err = 1; + } + } + if (err) // それ以外のエラーなら + goto error_end; + print_progress_done(); // 改行して行の先頭に戻しておく + //for (i = 0; i < block_lost; i++) + // printf("id[%d] = %d\n", i, id[i]); +#ifdef TIMER +time_matrix = GetTickCount() - time_matrix; +#endif + +#ifdef TIMER + err = 0; // IO method : 0=Auto, -2=Read all, -3=Read some, -4=GPU all, -5=GPU some + if (err == 0){ +#endif + if ((OpenCL_method != 0) && (block_size >= 65536) && (source_num >= 256) && (block_lost >= 32) && + ((source_num + block_lost) * (__int64)block_size > 1048576 * 512)){ + // ブロック数が多いなら、ブロックごとにスレッドを割り当てる (GPU を使う) + if (memory_use & 16){ + err = -4; // SSD なら Read all 方式でブロックが断片化しても速い + } else + if (read_block_num(block_lost, 2, 0, MEM_UNIT) != 0){ + err = -5; // HDD でメモリーが足りてるなら Read some 方式を使う + } else { + err = -4; // メモリー不足なら Read all 方式でブロックを断片化させる + } + } else { + // ソース・ブロックを全て断片的に読み込むか、いくつかを丸ごと読み込むかを決める + if (memory_use & 16){ + err = -2; // SSD なら Read all 方式でブロックが断片化しても速い + } else + if (read_block_num(block_lost, cpu_num - 1, 0, sse_unit) != 0){ + err = -3; // HDD でメモリーが足りてるなら Read some 方式を使う + } else { + err = -2; // メモリー不足なら Read all 方式でブロックを断片化させる + } + } +#ifdef TIMER + } +#endif + + // ファイル・アクセスの方式によって分岐する + if (err == -5) + err = decode_method5(file_path, block_lost, rcv_hFile, files, s_blk, p_blk, mat); + if (err == -4) + err = decode_method4(file_path, block_lost, rcv_hFile, files, s_blk, p_blk, mat); + if (err == -3) // ソース・データをいくつか読み込む場合 + err = decode_method3(file_path, block_lost, rcv_hFile, files, s_blk, p_blk, mat); + if (err == -2) // ソース・データを全て読み込む場合 + err = decode_method2(file_path, block_lost, rcv_hFile, files, s_blk, p_blk, mat); +#ifdef TIMER + if (err != 1){ + time_total = GetTickCount() - time_total; + printf("total %d.%03d sec\n", time_total / 1000, time_total % 1000); + printf("matrix %d.%03d sec\n", time_matrix / 1000, time_matrix % 1000); + } +#endif + +error_end: + if (mat) + free(mat); + galois_free_table(); // Galois Field のテーブルを解放する + return err; +} + diff --git a/source/par2j/reedsolomon.h b/source/par2j/reedsolomon.h new file mode 100644 index 0000000..4c30a92 --- /dev/null +++ b/source/par2j/reedsolomon.h @@ -0,0 +1,80 @@ +#ifndef _REEDSOLOMON_H_ +#define _REEDSOLOMON_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +//#define TIMER // 実験用 + +// Read all source & Keep some parity 方式 +// 部分的なエンコードを行う最低ブロック数 +#define PART_MAX_RATE 1 // ソース・ブロック数の 1/2 = 50% +#define PART_MIN_RATE 5 // ソース・ブロック数の 1/32 = 3.1% + +// Read some source & Keep all parity 方式 +// 一度に読み込む最少ブロック数 +#define READ_MIN_RATE 1 // 保持するブロック数の 1/2 = 50% +#define READ_MIN_NUM 16 + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// Cache Blocking を試みる +int try_cache_blocking(int unit_size); + +// 空きメモリー量からファイル・アクセスのバッファー・サイズを計算する +unsigned int get_io_size( + unsigned int buf_num, // 何ブロック分の領域を確保するのか + unsigned int *part_num, // 部分的なエンコード用の作業領域 + size_t trial_alloc, // 確保できるか確認するのか + int alloc_unit); // メモリー単位の境界 (sse_unit か MEM_UNIT) + +// 何ブロックまとめてファイルから読み込むかを空きメモリー量から計算する +int read_block_num( + int keep_num, // 保持するパリティ・ブロック数 + int add_num, // 余裕を見るブロック数 + size_t trial_alloc, // 確保できるか確認するのか + int alloc_unit); // メモリー単位の境界 (sse_unit か MEM_UNIT) + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// リード・ソロモン符号を使ってエンコードする +int rs_encode( + wchar_t *file_path, + unsigned char *header_buf, // Recovery Slice packet のパケット・ヘッダー + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_c *files, // ソース・ファイルの情報 + source_ctx_c *s_blk, // ソース・ブロックの情報 + parity_ctx_c *p_blk); // パリティ・ブロックの情報 + +// パリティ・ブロックをメモリー上に保持して、一度に読み書きする +int rs_encode_1pass( + wchar_t *file_path, + wchar_t *recovery_path, // 作業用 + int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限 + int block_distri, // パリティ・ブロックの分配方法 (3-bit目は番号の付け方) + int packet_num, // 共通パケットの数 + unsigned char *common_buf, // 共通パケットのバッファー + int common_size, // 共通パケットのバッファー・サイズ + unsigned char *footer_buf, // 末尾パケットのバッファー + int footer_size, // 末尾パケットのバッファー・サイズ + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_c *files, // ソース・ファイルの情報 + source_ctx_c *s_blk); // ソース・ブロックの情報 + +// リード・ソロモン符号を使ってデコードする +int rs_decode( + wchar_t *file_path, + int block_lost, // 失われたソース・ブロックの数 + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_r *files, // ソース・ファイルの情報 + source_ctx_r *s_blk, // ソース・ブロックの情報 + parity_ctx_r *p_blk); // パリティ・ブロックの情報 + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par2j/repair.c b/source/par2j/repair.c new file mode 100644 index 0000000..4827351 --- /dev/null +++ b/source/par2j/repair.c @@ -0,0 +1,879 @@ +// repair.c +// Copyright : 2022-10-14 Yutaka Sawada +// License : GPL + +#ifndef _UNICODE +#define _UNICODE +#endif +#ifndef UNICODE +#define UNICODE +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 // Windows Vista or later +#endif + +#include + +#include + +#include "common2.h" +#include "crc.h" +#include "md5_crc.h" +#include "ini.h" +#include "json.h" +#include "repair.h" + + +// ファイル・リストを表示する +void print_file_list( + char *ascii_buf, // 作業用 + file_ctx_r *files) +{ + int i; + + printf("\nInput File list :\n"); + printf(" Size Slice : Filename\n"); + fflush(stdout); + for (i = 0; i < file_num; i++){ + if (files[i].name < 0){ // File Description packet が欠落してる + printf(" ? ? : Unknown\n"); + } else { + if (files[i].name == 0){ // ファイル名だけ不明 + ascii_buf[0] = 0; + } else { + utf16_to_cp(list_buf + files[i].name, ascii_buf, cp_output); + } + // ファイルごとのブロック数と開始番号 + if ((i < entity_num) && (files[i].size > 0)){ + printf("%13I64d %6d : \"%s\"\n", files[i].size, files[i].b_num, ascii_buf); + } else { // 空のファイルやフォルダ、または non recovery set + printf("%13I64d 0 : \"%s\"\n", files[i].size, ascii_buf); + } + } + } + printf("\nInput File total size\t: %I64d\n", total_file_size); + printf("Input File Slice count\t: %d\n", source_num); +} + +// ディレクトリ記号までの長さを返す (存在しない場合は -1) +static int wcschr_dir(wchar_t *s) +{ + int i = 0; + + while (s[i] != 0){ + if (s[i] == '\\') + return i; + i++; + } + + return -1; +} + +// ファイル名を比較する +static int name_cmp(const void *elem1, const void *elem2) +{ + wchar_t *name1, *name2; + int rv = 2, len1, len2; + + name1 = list_buf + ((file_ctx_r *)elem1)->name; + name2 = list_buf + ((file_ctx_r *)elem2)->name; + + while (name1[0] + name2[0] != 0){ + len1 = wcschr_dir(name1); + len2 = wcschr_dir(name2); + + // フォルダを先にする + if (len1 >= 0){ + if (len2 < 0) // file1 だけサブ・ディレクトリがある + return -1; + } else if (len2 >= 0){ // file2 だけサブ・ディレクトリがある + return 1; + } + + // ユーザーの言語設定によって順序を決める + //rv = CompareString(LOCALE_USER_DEFAULT, 0, name1, len1, name2, len2); + rv = CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0x00000008, name1, len1, name2, len2, NULL, NULL, 0); + if ((rv == 2) && (len1 != -1) && (len2 != -1)){ + name1 += len1 + 1; + name2 += len2 + 1; + } else { + break; + } + } + + return rv - 2; +// return wcscmp(name1, name2); // QuickPar はこちらの順序 +} + +// ソース・ファイル情報を確認して集計する +int set_file_data( + char *ascii_buf, // 作業用 + file_ctx_r *files) +{ + int i, bad_flag; + + // ソース・ファイルの基本情報 + bad_flag = 0; + total_file_size = 0; + source_num = 0; + for (i = 0; i < file_num; i++){ + if (files[i].name < 0){ // File Description packet が欠落してる + bad_flag |= 1; + } else { + if (files[i].name == 0){ // ファイル名だけ不明 + bad_flag |= 2; + } else { + if (base_len + wcslen(list_buf + files[i].name) >= MAX_LEN - ADD_LEN) + bad_flag |= 4; // ファイル・パスが長過ぎる + } + // ファイルごとのブロック数と開始番号 + if ((i < entity_num) && (files[i].size > 0)){ + files[i].b_off = source_num; + files[i].b_num = (int)((files[i].size + (__int64)block_size - 1) / (__int64)block_size); + source_num += files[i].b_num; + files[i].state = 0x80; // チェックサムが必要だがまだ読み取ってない + } else { // 空のファイルやフォルダ、または non recovery set + files[i].b_off = 0; + files[i].b_num = 0; + files[i].state = 0; // チェックサムは必要ない + } + total_file_size += files[i].size; + } + } + if (bad_flag){ + print_file_list(ascii_buf, files); // 終了前にファイル・リストを表示する + if (bad_flag & 1){ // File Description packet が不足してると検査を継続できない + printf("\nFile Description packet is missing\n"); + } else if (bad_flag & 2){ + printf("\nfilename is unknown\n"); + } else if (bad_flag & 4){ + printf("\nfilename is too long\n"); + } + return 1; + } + + // PAR2 仕様ではソース・ブロックの最大値は 32768 個だが、 + // 規格外のリカバリ・データがあるかもしれない (YencPowerPost A&A v11b のバグ) + if (source_num > MAX_SOURCE_NUM){ + parity_num = 0; // 規格外ならリカバリ・ブロックを無効にする + } else { + // リカバリ・ブロック自体はいくらでも作れるが、異なるスライスは 65535個まで + parity_num = MAX_PARITY_NUM; + } + + // If you want to see original order (sorted by File ID), comment out below lines. + // ファイルを並び替えたら、ソース・ブロックのファイル番号もそれに応じて変えること + // recovery set のファイルをファイル名の順に並び替える + if (entity_num > 1) + qsort(files, entity_num, sizeof(file_ctx_r), name_cmp); + // non-recovery set のファイルをファイル名の順に並び替える + if (file_num - entity_num > 1) + qsort(&(files[entity_num]), file_num - entity_num, sizeof(file_ctx_r), name_cmp); + + print_file_list(ascii_buf, files); // 並び替えられたファイル・リストを表示する + + return 0; +} + +// ソース・ファイルの検査結果を集計して、修復方法を判定する +int result_file_state( + char *ascii_buf, + int *result, + int parity_now, + int recovery_lost, + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + int i, num, find_num, b_last; + int lost_num, need_repair, rejoin_num, repair_num, incomp_num; + + find_num = -1; // 項目が未表示の印 + need_repair = 0; + rejoin_num = 0; + repair_num = 0; + incomp_num = 0; + for (num = 0; num < entity_num; num++){ + if (files[num].size == 0){ // フォルダまたは空ファイル + if (files[num].state & 0x3F) // 空ファイルとフォルダが存在する (0x40) 以外なら + need_repair++; + } else { // ソース・ファイル + if (files[num].state & 0x03){ // 消失(0x01)、破損(0x02) ならスライス検出結果を表示する + if (find_num < 0){ // 項目表示がまだなら + printf("\nCounting available slice:\n"); + printf(" Avail / Slice : Filename\n"); // 集計された検出スライス数 + } + utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); + find_num = 0; + if ((files[num].state & 0x80) == 0){ // チェックサムが存在するなら + b_last = files[num].b_off + files[num].b_num; + for (i = files[num].b_off; i < b_last; i++){ + if (s_blk[i].exist != 0) + find_num++; + } + } + printf("%6d / %6d : \"%s\"\n", find_num, files[num].b_num, ascii_buf); + if (find_num == files[num].b_num){ + need_repair |= 0x20000000; // スライスが揃ってるのでファイルを再構築できる + rejoin_num++; + } else { + need_repair |= 0x10000000; // スライスが足りない + repair_num++; + } + } else if (files[num].state & 0x30){ // 追加(0x10)、別名・移動(0x20, 0x28) + need_repair++; + } + } + } + for (num = entity_num; num < file_num; num++){ + if (files[num].state & 0x30){ // 追加(0x10)、別名・移動(0x20)、フォルダが移動(0x60) + need_repair++; + } else if (files[num].state & 0x03){ // 消失(0x01)、破損(0x02)、フォルダが消失(0x41) + if (files[num].size == 0){ // サイズが 0ならすぐに復元できる + need_repair++; + } else { + need_repair |= 0x40000000; // non-recovery set のファイルが破損して修復できない + incomp_num++; + } + } + } + json_file_state(files); // JSONファイルに記録する + + // 利用可能なソース・ブロックの数 + printf("\nInput File Slice avail\t: %d\n", first_num); + // 消失したソース・ブロックの数 + lost_num = source_num - first_num; + printf("Input File Slice lost\t: %d\n\n", lost_num); + if (need_repair == 0) + printf("All Files Complete\n"); + if (recovery_lost > 0){ // 不完全なリカバリ・ファイルがあるなら + i = 256; + printf("%d PAR File(s) Incomplete\n", recovery_lost); + } else { + i = 0; + } + + // 修復する必要があるかどうか + if (need_repair != 0){ + i |= 4; + if (need_repair & 0x0FFFFFFF){ // 簡易修復は可能 + printf("Ready to rename %d file(s)\n", need_repair & 0x0FFFFFFF); + i |= 32; + } + if (need_repair & 0x20000000){ // 再構築までは可能 + printf("Ready to rejoin %d file(s)\n", rejoin_num); + i |= 64; + } + if (need_repair & 0x10000000){ // ソース・ブロックの復元が必要 + if (lost_num > parity_now){ + printf("Need %d more slice(s) to repair %d file(s)\n", lost_num - parity_now, repair_num); + i |= 8; + } else if ((lost_num == parity_now) && (lost_num >= 2)){ // 逆行列の計算で失敗するかも + printf("Try to repair %d file(s)\n", repair_num); + i |= 128 | 8; + } else { + printf("Ready to repair %d file(s)\n", repair_num); + i |= 128; + } + } + if (need_repair & 0x40000000) // non-recovery set のファイルは修復できない + printf("Cannot repair %d file(s)\n", incomp_num); + } + fflush(stdout); + + *result = need_repair; + return i; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 簡単な修復を行う、まだ修復の必要なファイルの数を戻す +int simple_repair( + char *ascii_buf, + int need_repair, + file_ctx_r *files) // 各ソース・ファイルの情報 +{ + wchar_t file_path[MAX_LEN], old_path[MAX_LEN]; + int i, num, repaired_num; + HANDLE hFile; + + if (need_repair){ // 簡単な修復だけでいいファイルの数 + printf("\nCorrecting file : %d\n", need_repair); + printf(" Status : Filename\n"); + fflush(stdout); + need_repair = 0x10000000; + } + repaired_num = 0; + wcscpy(file_path, base_dir); + wcscpy(old_path, base_dir); + + // recovery set のファイル + for (num = 0; num < entity_num; num++){ + utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); + wcscpy(file_path + base_len, list_buf + files[num].name); + if (files[num].size == 0){ // フォルダまたは空ファイルを作り直す + switch (files[num].state){ + case 1: // 存在しなくてもサイズが 0ならすぐに復元できる + hFile = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() == ERROR_PATH_NOT_FOUND)){ // Path not found (3) + make_dir(file_path); // 途中のフォルダが存在しないのなら作成する + hFile = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } + if (hFile != INVALID_HANDLE_VALUE){ + CloseHandle(hFile); + files[num].state = 0; + printf(" Restored : \"%s\"\n", ascii_buf); + repaired_num++; + } else { + printf(" Failed : \"%s\"\n", ascii_buf); + } + fflush(stdout); + break; + case 16: // ファイルに内容がある場合は破損ではなく追加と見なす + if (shorten_file(file_path, 0) == 0){ + files[num].state = 0; + printf(" Restored : \"%s\"\n", ascii_buf); + repaired_num++; + } else { + printf(" Failed : \"%s\"\n", ascii_buf); + } + fflush(stdout); + break; + case 32: // 本来の場所に戻す + case 96: // フォルダを本来の場所に戻す + wcscpy(old_path + base_len, list_buf + files[num].name2); + if (replace_file(file_path, old_path) == 0){ + files[num].state &= 0x40; // 0 か 64 になる + printf(" Restored : \"%s\"\n", ascii_buf); + repaired_num++; + } else { // ファイル名の変更に失敗した + printf(" Failed : \"%s\"\n", ascii_buf); + } + fflush(stdout); + break; + case 65: // フォルダが消失 + i = (int)wcslen(file_path); + if (file_path[i - 1] == '\\') + file_path[i - 1] = 0; + if (CreateDirectory(file_path, NULL) == 0){ + i = GetLastError(); + if (i == ERROR_PATH_NOT_FOUND){ // Path not found (3) + make_dir(file_path); // 途中のフォルダが存在しないのなら作成する + i = CreateDirectory(file_path, NULL); + } else if (i == ERROR_ALREADY_EXISTS){ // Destination file is already exist (183) + if ((GetFileAttributes(file_path) & FILE_ATTRIBUTE_DIRECTORY) == 0){ + // 同名のファイルが存在するならどかす + move_away_file(file_path); + i = CreateDirectory(file_path, NULL); + } + } + } + if (i != 0){ + files[num].state = 64; + printf(" Restored : \"%s\"\n", ascii_buf); + repaired_num++; + } else { + printf(" Failed : \"%s\"\n", ascii_buf); + } + fflush(stdout); + break; + } + } else { + switch (files[num].state & 0x7F){ // チェックサムの有無に関係なく訂正できる + case 1: // 後で失われたブロックを復元する + case 2: + case 6: + need_repair++; + break; + case 16: // 末尾のゴミを取り除く + if (shorten_file(file_path, files[num].size) == 0){ + write_ini_complete(num, file_path); + files[num].state &= 0x80; + printf(" Restored : \"%s\"\n", ascii_buf); + repaired_num++; + } else { + printf(" Failed : \"%s\"\n", ascii_buf); + } + fflush(stdout); + break; + case 32: // ファイル名を訂正する + case 40: + wcscpy(old_path + base_len, list_buf + files[num].name2); + if (replace_file(file_path, old_path) == 0){ + files[num].state &= 0x80; + printf(" Restored : \"%s\"\n", ascii_buf); + repaired_num++; + } else { // ファイル名の変更に失敗した + printf(" Failed : \"%s\"\n", ascii_buf); + } + fflush(stdout); + break; + } + } + } + + // non-recovery set のファイル + for (num = entity_num; num < file_num; num++){ + utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); + wcscpy(file_path + base_len, list_buf + files[num].name); + switch (files[num].state){ + case 1: // 消失 + case 2: // 破損 + if (files[num].size == 0){ // サイズが 0ならすぐに復元できる + hFile = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if ((hFile == INVALID_HANDLE_VALUE) && (GetLastError() == ERROR_PATH_NOT_FOUND)){ // Path not found (3) + make_dir(file_path); // 途中のフォルダが存在しないのなら作成する + hFile = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } + if (hFile != INVALID_HANDLE_VALUE){ + CloseHandle(hFile); + files[num].state = 0; + printf(" Restored : \"%s\"\n", ascii_buf); + repaired_num++; + } else { + printf(" Failed : \"%s\"\n", ascii_buf); + } + fflush(stdout); + } + break; + case 16: // 追加 + if (shorten_file(file_path, files[num].size) == 0){ + files[num].state = 0; + printf(" Restored : \"%s\"\n", ascii_buf); + repaired_num++; + } else { + printf(" Failed : \"%s\"\n", ascii_buf); + } + fflush(stdout); + break; + case 32: // 本来の場所に戻す、またはファイル名を訂正する + case 40: + case 96: // フォルダを本来の場所に戻す + wcscpy(old_path + base_len, list_buf + files[num].name2); + if (replace_file(file_path, old_path) == 0){ + files[num].state &= 0x40; // 0 か 64 になる + printf(" Restored : \"%s\"\n", ascii_buf); + repaired_num++; + } else { // ファイル名の変更に失敗した + printf(" Failed : \"%s\"\n", ascii_buf); + } + fflush(stdout); + break; + case 65: // フォルダが消失 + i = (int)wcslen(file_path); + if (file_path[i - 1] == '\\') + file_path[i - 1] = 0; + if (CreateDirectory(file_path, NULL) == 0){ + i = GetLastError(); + if (i == ERROR_PATH_NOT_FOUND){ // Path not found (3) + make_dir(file_path); // 途中のフォルダが存在しないのなら作成する + i = CreateDirectory(file_path, NULL); + } else if (i == ERROR_ALREADY_EXISTS){ // Destination file is already exist (183) + if ((GetFileAttributes(file_path) & FILE_ATTRIBUTE_DIRECTORY) == 0){ + // 同名のファイルが存在するならどかす + move_away_file(file_path); + i = CreateDirectory(file_path, NULL); + } + } + } + if (i != 0){ + files[num].state = 64; + printf(" Restored : \"%s\"\n", ascii_buf); + repaired_num++; + } else { + printf(" Failed : \"%s\"\n", ascii_buf); + } + fflush(stdout); + break; + } + } + + if (need_repair & 0x10000000) // 修復できたファイルの数 + printf("\nRestored file count\t: %d\n", repaired_num); + + return need_repair & 0x0FFFFFFF; +} + +// 4バイトのソース・ブロックを逆算してソース・ファイルに書き込む +int restore_block4( + wchar_t *file_path, + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + int i, j, num, b_last; + unsigned int data; + unsigned int time_last = 0; + HANDLE hFile; + + print_progress_text(0, "Restoring slice"); + wcscpy(file_path, base_dir); + for (num = 0; num < entity_num; num++){ + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((num * 1000) / entity_num)) + return 2; + time_last = GetTickCount(); + } + + if ((files[num].size > 0) && ((files[num].state & 0x80) == 0) && + ((files[num].state & 3) != 0)){ // 不完全なファイルにチェックサムが存在するなら + //printf("file %d, 0x%08x\n", num, files[num].state); + if (files[num].state & 4){ // 破損ファイルを上書きして復元する場合 + // ソース・ファイルを作り直す(元のデータは全て消える) + wcscpy(file_path + base_len, list_buf + files[num].name); + hFile = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + } else { + // 作業ファイルを開く + hFile = handle_temp_file(list_buf + files[num].name, file_path); + } + if (hFile == INVALID_HANDLE_VALUE) + return 1; + + // 逆算したソース・ブロックを書き込んでいく + b_last = files[num].b_off + files[num].b_num; + for (i = files[num].b_off; i < b_last; i++){ + data = crc_reverse_zero(s_blk[i].crc, 4); // CRC-32 からブロック内容を逆算する + if (!WriteFile(hFile, &data, s_blk[i].size, &j, NULL)){ + print_win32_err(); + CloseHandle(hFile); + return 1; + } + } + CloseHandle(hFile); + } + } + print_progress_done(); // 改行して行の先頭に戻しておく + + return 0; +} + +// 同じ内容のソース・ブロックを流用する、または内容がわかるブロックは逆算する +int restore_block( + wchar_t *file_path, + int reuse_num, // 流用可能なソース・ブロックの数 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + int i, num, src_blk, src_file; + unsigned int data; + unsigned int time_last = 0, prog_num = 0; + __int64 file_off; + HANDLE hFile, hFile_src; + + print_progress_text(0, "Restoring slice"); + wcscpy(file_path, base_dir); + for (num = 0; num < entity_num; num++){ + if ((files[num].size > 0) && ((files[num].state & 0x80) == 0) && + ((files[num].state & 3) != 0)){ // チェックサムと作業ファイルが存在するなら + hFile = NULL; + + // 利用可能なソース・ブロックをコピーしていく + i = files[num].b_off; + for (file_off = 0; file_off < files[num].size; file_off += block_size){ + if ((s_blk[i].exist >= 3) && (s_blk[i].exist <= 5)){ + if (hFile == NULL){ // 書き込み先ファイルがまだ開かれてなければ + if (files[num].state & 4){ // 破損ファイルを上書きして復元する場合 + // 上書き用のソース・ファイルを開く + hFile = handle_write_file(list_buf + files[num].name, file_path, files[num].size); + } else { + // 作業ファイルを開く + hFile = handle_temp_file(list_buf + files[num].name, file_path); + } + if (hFile == INVALID_HANDLE_VALUE) + return 1; + } + + switch (s_blk[i].exist){ + case 3: // 内容が全て 0 のブロック + // 0 で埋める + if (file_fill_data(hFile, file_off, 0, s_blk[i].size)){ + CloseHandle(hFile); + printf("file_fill_data, %d\n", i); + return 1; + } + break; + case 4: // 同じファイル、または別のファイルに存在する同じブロック + src_blk = s_blk[i].file; // s_blk[i].file にはそのブロック番号が入ってる + src_file = s_blk[src_blk].file; +/* printf("copy block : off 0x%I64X block %d -> off 0x%I64X block %d\n", + (__int64)(src_blk - files[src_file].b_off) * (__int64)block_size, + src_blk, file_off, i);*/ + if (files[src_file].state & 7){ // 読み込み元が消失・破損ファイルなら + if (files[src_file].state & 4){ // 上書き中の破損ファイルから読み込む + wcscpy(file_path + base_len, list_buf + files[src_file].name); + } else { // 作り直した作業ファイルから読み込む + get_temp_name(list_buf + files[src_file].name, file_path + base_len); + } + hFile_src = CreateFile(file_path, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + } else { + if (files[src_file].state & 0x20){ // 名前訂正失敗時には別名ファイルから読み込む + wcscpy(file_path + base_len, list_buf + files[src_file].name2); + } else { // 完全なソース・ファイルから読み込む (追加訂正失敗時も) + wcscpy(file_path + base_len, list_buf + files[src_file].name); + } + hFile_src = CreateFile(file_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); + } + if (hFile_src == INVALID_HANDLE_VALUE){ + print_win32_err(); + CloseHandle(hFile); + printf_cp("cannot open file, %s\n", file_path); + return 1; + } + // コピーする + if (file_copy_data(hFile_src, (__int64)(src_blk - files[src_file].b_off) * (__int64)block_size, + hFile, file_off, s_blk[i].size)){ + print_win32_err(); + CloseHandle(hFile); + printf("file_copy_data, %d\n", i); + return 1; + } + CloseHandle(hFile_src); + break; + case 5: // 内容を逆算することができるブロック + data = crc_reverse_zero(s_blk[i].crc, block_size); // CRC-32 からブロック内容を逆算する + if (file_write_data(hFile, file_off, (unsigned char *)(&data), s_blk[i].size)){ + CloseHandle(hFile); + printf("file_write_data, %d\n", i); + return 1; + } + break; + } + s_blk[i].file = num; + + // 経過表示 + prog_num++; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((prog_num * 1000) / reuse_num)) + return 2; + time_last = GetTickCount(); + } + } + i++; + } + if (hFile) + CloseHandle(hFile); + } + } + print_progress_done(); // 改行して行の先頭に戻しておく + + return 0; +} + +// 正しく修復できたか調べて結果表示する +int verify_repair( + wchar_t *file_path, + char *ascii_buf, + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + wchar_t temp_path[MAX_LEN]; + int i, num, b_last, bad_flag, repaired_num; + + repaired_num = 0; + wcscpy(file_path, base_dir); + for (num = 0; num < entity_num; num++){ + if (files[num].size == 0) + continue; // 空ファイルは検証しない + + if (files[num].state & 4){ // 破損ファイルを上書きして修復したなら + bad_flag = 0; + // 再度開きなおす + wcscpy(file_path + base_len, list_buf + files[num].name); + if (files[num].state & 0x80){ // チェックサムが欠落したソース・ファイル + i = file_hash_direct(num, file_path, list_buf + files[num].name, files, NULL); + } else { + i = file_hash_direct(num, file_path, list_buf + files[num].name, files, s_blk); + } + if (i == -2) + return 2; // 確認中にキャンセルされた + if (i == -4){ + bad_flag = 1; // Missing + } else if (i != -3){ + bad_flag = 2; // Failed + } + + // 結果を表示する + utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); + if (bad_flag){ // 失敗 + printf(" Failed : \"%s\"\n", ascii_buf); + } else { // 修復成功 + write_ini_complete(num, file_path); + files[num].state &= 0x80; + b_last = files[num].b_off + files[num].b_num; + for (i = files[num].b_off; i < b_last; i++){ + if (s_blk[i].exist == 0) + first_num++; // 復元したブロック数 + s_blk[i].exist = 1; + } + printf(" Repaired : \"%s\"\n", ascii_buf); + repaired_num++; + } + fflush(stdout); + + } else if (files[num].state & 3){ // 新しく作り直したソース・ファイルなら + bad_flag = 0; + // 再度開きなおす + wcscpy(file_path + base_len, list_buf + files[num].name); + get_temp_name(file_path, temp_path); + if (files[num].state & 0x80){ // チェックサムが欠落したソース・ファイル + i = file_hash_direct(num, temp_path, list_buf + files[num].name, files, NULL); + } else { + i = file_hash_direct(num, temp_path, list_buf + files[num].name, files, s_blk); + } + if (i == -2) + return 2; // 確認中にキャンセルされた + if (i == -4){ + bad_flag = 1; // Missing + } else if (i != -3){ + bad_flag = 2; // Failed + } + + // 結果を表示する + utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); + if (bad_flag){ // 失敗 + if (((files[num].state & 0x80) == 0) && + (bad_flag == 2) && ((switch_b & 4) != 0)){ // 修復に失敗した場合でも、元のファイルを置き換える + // 完全なブロックが含まれてるかどうか + b_last = files[num].b_off + files[num].b_num; + for (i = files[num].b_off; i < b_last; i++){ + if (s_blk[i].exist != 0){ + i = -1; + break; + } + } + if (i < 0){ // 消失ファイルを代替し、破損ファイルを置き換える + if (replace_file(file_path, temp_path) == 0){ + files[num].state = 2; // 破損ファイルにする + printf(" Replaced : \"%s\"\n", ascii_buf); + fflush(stdout); + continue; + } + } + } + printf(" Failed : \"%s\"\n", ascii_buf); + } else { // 修復成功 + if (replace_file(file_path, temp_path) == 0){ // 修復したファイルを戻す + write_ini_complete(num, file_path); + files[num].state &= 0x80; + b_last = files[num].b_off + files[num].b_num; + for (i = files[num].b_off; i < b_last; i++){ + if (s_blk[i].exist == 0) + first_num++; // 復元したブロック数 + s_blk[i].exist = 1; + } + printf(" Repaired : \"%s\"\n", ascii_buf); + repaired_num++; + } else { // ファイルを戻せなかった場合は、別名扱いにして修復したファイルを残す + files[num].state = (files[num].state & 0x80) | 0x20; + printf(" Locked : \"%s\"\n", ascii_buf); + } + } + fflush(stdout); + } + } + + // 修復できたファイルの数と修復後のブロック数 + printf("\nRepaired file count\t: %d\n", repaired_num); + printf("Input File Slice avail\t: %d\n", first_num); + + return 0; +} + +// 作業用のソース・ファイルを削除する +void delete_work_file( + wchar_t *file_path, + file_ctx_r *files) // 各ソース・ファイルの情報 +{ + int num; + + wcscpy(file_path, base_dir); + for (num = 0; num < entity_num; num++){ + if (files[num].size == 0) + continue; + + //printf("files[%d].state = %d\n", num, files[num].state); + if (((files[num].state & 3) != 0) && ((files[num].state & 4) == 0)){ // 作業ファイルが存在するなら + // 作業ファイルを削除する + get_temp_name(list_buf + files[num].name, file_path + base_len); + //printf_cp("delete %s\n", file_path); + if (DeleteFile(file_path) == 0){ + //printf("error = %d\n", GetLastError()); + // Anti-Virusソフトが書き込み直後のファイルを検査してロックすることがある + if (GetLastError() == 32){ // ERROR_SHARING_VIOLATION + Sleep(100); // 少し待ってから再挑戦する + DeleteFile(file_path); + } + } + } + } +} + +// ブロック単位の復元ができなくても、再構築したファイルで置き換える +void replace_incomplete( + wchar_t *file_path, + char *ascii_buf, + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + wchar_t temp_path[MAX_LEN]; + int i, num, b_last, first_time; + + first_time = 1; + wcscpy(file_path, base_dir); + for (num = 0; num < entity_num; num++){ + if ((files[num].size > 0) && ((files[num].state & 0x80) == 0) && + ((files[num].state & 3) != 0)){ // チェックサムと作業ファイルが存在するなら + // 完全なブロックが含まれてるかどうか + b_last = files[num].b_off + files[num].b_num; + for (i = files[num].b_off; i < b_last; i++){ + if (s_blk[i].exist != 0){ + i = -1; + break; + } + } + if (i >= 0) + continue; // 完全なブロックを全く見つけれなかった場合はだめ + + // 作業ファイルが存在すれば、消失ファイルを代替し、破損ファイルを置き換える + wcscpy(file_path + base_len, list_buf + files[num].name); + get_temp_name(file_path, temp_path); + if (replace_file(file_path, temp_path) == 0){ + files[num].state = 2; // 破損ファイルにする + if (first_time){ + printf("\nPutting incomplete file :\n"); + printf(" Status : Filename\n"); + first_time = 0; + } + utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); + printf(" Replaced : \"%s\"\n", ascii_buf); + } + } + } +} + +// リカバリ・ファイルを削除する(Useless状態だったのは無視する) +int purge_recovery_file(void) +{ + int err, num, recv_off; + + //printf("recovery_num = %d\n", recovery_num); + err = num = 0; + recv_off = 0; + while (recv_off < recv2_len){ + //printf_cp("delete %s\n", recv2_buf + recv_off); + // 標準でゴミ箱に入れようとする、失敗したら普通に削除する + if (delete_file_recycle(recv2_buf + recv_off) == 0){ + num++; + } else { + err++; + } + + // 次のファイルの位置にずらす + while (recv2_buf[recv_off] != 0) + recv_off++; + recv_off++; + } + printf("%d PAR File(s) Deleted\n", num); + + return err; +} + diff --git a/source/par2j/repair.h b/source/par2j/repair.h new file mode 100644 index 0000000..cc0fe7f --- /dev/null +++ b/source/par2j/repair.h @@ -0,0 +1,71 @@ +#ifndef _REPAIR_H_ +#define _REPAIR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +// ソース・ファイル情報を確認して集計する +int set_file_data( + char *ascii_buf, // 作業用 + file_ctx_r *files); + +// ソース・ファイルの検査結果を集計して、修復方法を判定する +int result_file_state( + char *ascii_buf, + int *result, + int parity_now, + int recovery_lost, + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 簡単な修復を行う +int simple_repair( + char *ascii_buf, + int need_repair, + file_ctx_r *files); // 各ソース・ファイルの情報 + +// 4バイトのソース・ブロックを逆算してソース・ファイルに書き込む +int restore_block4( + wchar_t *file_path, + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 + +// ソース・ブロックを流用または逆算してソース・ファイルに書き込む +int restore_block( + wchar_t *file_path, + int reuse_num, // 流用可能なソース・ブロックの数 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 + +// 正しく修復できたか調べて結果表示する +int verify_repair( + wchar_t *file_path, + char *ascii_buf, + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 + +// 作業用のソース・ファイルを削除する +void delete_work_file( + wchar_t *file_path, + file_ctx_r *files); // 各ソース・ファイルの情報 + +// ブロック単位の復元ができなくても、再構築したファイルで置き換える +void replace_incomplete( + wchar_t *file_path, + char *ascii_buf, + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 + +// リカバリ・ファイルを削除する(Useless状態だったのは無視する) +int purge_recovery_file(void); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par2j/res_par2j.rc b/source/par2j/res_par2j.rc new file mode 100644 index 0000000..4cfdeec --- /dev/null +++ b/source/par2j/res_par2j.rc @@ -0,0 +1,26 @@ +1 RT_STRING ".\\source.cl" + +1 VERSIONINFO +FILEVERSION 1,3,2,8 +PRODUCTVERSION 1,3,2,0 +FILEOS 0x40004 +FILETYPE 0x1 +{ +BLOCK "StringFileInfo" +{ + BLOCK "040904B0" + { + VALUE "FileDescription", "PAR2 client" + VALUE "LegalCopyright", "Copyright (C) 2023 Yutaka Sawada" + VALUE "ProductName", "par2j" + VALUE "FileVersion", "1.3.2.8" + VALUE "ProductVersion", "1.3.2.0" + } +} + +BLOCK "VarFileInfo" +{ + VALUE "Translation", 0x0409 0x04B0 +} +} + diff --git a/source/par2j/rs_decode.c b/source/par2j/rs_decode.c new file mode 100644 index 0000000..e9b0606 --- /dev/null +++ b/source/par2j/rs_decode.c @@ -0,0 +1,2186 @@ +// rs_decode.c +// Copyright : 2022-10-08 Yutaka Sawada +// License : GPL + +#ifndef _UNICODE +#define _UNICODE +#endif +#ifndef UNICODE +#define UNICODE +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 // Windows Vista or later +#endif + +#include +#include +#include + +#include + +#include "common2.h" +#include "crc.h" +#include "gf16.h" +#include "phmd5.h" +#include "lib_opencl.h" +#include "reedsolomon.h" +#include "rs_decode.h" + + +#ifdef TIMER +static unsigned int time_start, time_read = 0, time_write = 0, time_calc = 0; +static unsigned int read_count, write_count = 0, skip_count; +#endif + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// 非同期 IO + +typedef struct { // RS threading control struct + unsigned short * volatile mat; // 行列 + unsigned char * volatile buf; + volatile unsigned int size; // バイト数 + volatile int count; + volatile int off; + volatile int now; + HANDLE run; + HANDLE end; +} RS_TH; + +// chunk ごとに計算するためのスレッド +static DWORD WINAPI thread_decode2(LPVOID lpParameter) +{ + unsigned char *s_buf, *p_buf, *work_buf; + unsigned short *factor, *factor2; + int i, j, src_start, src_num, max_num; + int chunk_num, part_start, part_num, cover_num; + unsigned int unit_size, len, off, chunk_size; + HANDLE hRun, hEnd; + RS_TH *th; +#ifdef TIMER +unsigned int loop_count2a = 0, loop_count2b = 0; +unsigned int time_start2, time_encode2a = 0, time_encode2b = 0; +#endif + + th = (RS_TH *)lpParameter; + p_buf = th->buf; + unit_size = th->size; + chunk_size = th->off; + part_num = th->count; + hRun = th->run; + hEnd = th->end; + //_mm_sfence(); + SetEvent(hEnd); // 設定完了を通知する + + chunk_num = (unit_size + chunk_size - 1) / chunk_size; + + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + while (th->now < INT_MAX / 2){ +#ifdef TIMER +time_start2 = GetTickCount(); +#endif + s_buf = th->buf; + factor = th->mat; + len = chunk_size; + + if (th->size == 0){ // ソース・ブロック読み込み中 + src_start = th->off; // ソース・ブロック番号 + max_num = chunk_num * part_num; + // パリティ・ブロックごとに掛け算して追加していく + while ((j = InterlockedIncrement(&(th->now))) < max_num){ // j = ++th_now + off = j / part_num; // chunk の番号 + j = j % part_num; // lost block の番号 + off *= chunk_size; + if (off + len > unit_size) + len = unit_size - off; // 最後の chunk だけサイズが異なるかも + if (src_start == 0) // 最初のブロックを計算する際に + memset(p_buf + ((size_t)unit_size * j + off), 0, len); // ブロックを 0で埋める + galois_align_multiply(s_buf + off, p_buf + ((size_t)unit_size * j + off), len, factor[source_num * j]); +#ifdef TIMER +loop_count2a++; +#endif + } +#ifdef TIMER +time_encode2a += GetTickCount() - time_start2; +#endif + } else { // 消失ブロックを部分的に保持する場合 + // スレッドごとに復元する消失ブロックの chunk を変える + src_num = source_num - th->off; + cover_num = th->size; + part_start = th->count; + max_num = chunk_num * cover_num; + while ((j = InterlockedIncrement(&(th->now))) < max_num){ // j = ++th_now + off = j / cover_num; // chunk の番号 + j = j % cover_num; // lost block の番号 + off *= chunk_size; // chunk の位置 + if (off + len > unit_size) + len = unit_size - off; // 最後の chunk だけサイズが異なるかも + work_buf = p_buf + (size_t)unit_size * j + off; + if (part_start != 0) + memset(work_buf, 0, len); // 最初の part_num 以降は 2nd encode だけなので 0で埋める + factor2 = factor + source_num * (part_start + j); + + // ソース・ブロックごとにパリティを追加していく + for (i = 0; i < src_num; i++) + galois_align_multiply(s_buf + ((size_t)unit_size * i + off), work_buf, len, factor2[i]); +#ifdef TIMER +loop_count2b += src_num; +#endif + } +#ifdef TIMER +time_encode2b += GetTickCount() - time_start2; +#endif + } + //_mm_sfence(); // メモリーへの書き込みを完了する + SetEvent(hEnd); // 計算終了を通知する + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + } +#ifdef TIMER +loop_count2a /= chunk_num; // chunk数で割ってブロック数にする +loop_count2b /= chunk_num; +printf("sub-thread : total loop = %d\n", loop_count2a + loop_count2b); +if (time_encode2a > 0){ + i = (int)((__int64)loop_count2a * unit_size * 125 / ((__int64)time_encode2a * 131072)); +} else { + i = 0; +} +if (loop_count2a > 0) + printf(" 1st decode %d.%03d sec, %d loop, %d MB/s\n", time_encode2a / 1000, time_encode2a % 1000, loop_count2a, i); +if (time_encode2b > 0){ + i = (int)((__int64)loop_count2b * unit_size * 125 / ((__int64)time_encode2b * 131072)); +} else { + i = 0; +} +printf(" 2nd decode %d.%03d sec, %d loop, %d MB/s\n", time_encode2b / 1000, time_encode2b % 1000, loop_count2b, i); +#endif + + // 終了処理 + CloseHandle(hRun); + CloseHandle(hEnd); + return 0; +} + +static DWORD WINAPI thread_decode3(LPVOID lpParameter) +{ + unsigned char *s_buf, *p_buf, *work_buf; + unsigned short *factor, *factor2; + int i, j, block_lost, src_start, src_num, max_num, chunk_num; + unsigned int unit_size, len, off, chunk_size; + HANDLE hRun, hEnd; + RS_TH *th; +#ifdef TIMER +unsigned int loop_count2a = 0, loop_count2b = 0; +unsigned int time_start2, time_encode2a = 0, time_encode2b = 0; +#endif + + th = (RS_TH *)lpParameter; + p_buf = th->buf; + unit_size = th->size; + chunk_size = th->off; + block_lost = th->count; + hRun = th->run; + hEnd = th->end; + //_mm_sfence(); + SetEvent(hEnd); // 設定完了を通知する + + chunk_num = (unit_size + chunk_size - 1) / chunk_size; + max_num = chunk_num * block_lost; + + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + while (th->now < INT_MAX / 2){ +#ifdef TIMER +time_start2 = GetTickCount(); +#endif + s_buf = th->buf; + factor = th->mat; + len = chunk_size; + + if (th->size == 0){ // ソース・ブロック読み込み中 + src_start = th->off; // ソース・ブロック番号 + // パリティ・ブロックごとに掛け算して追加していく + while ((j = InterlockedIncrement(&(th->now))) < max_num){ // j = ++th_now + off = j / block_lost; // chunk の番号 + j = j % block_lost; // lost block の番号 + off *= chunk_size; + if (off + len > unit_size) + len = unit_size - off; // 最後の chunk だけサイズが異なるかも + if (src_start == 0) // 最初のブロックを計算する際に + memset(p_buf + ((size_t)unit_size * j + off), 0, len); // ブロックを 0で埋める + galois_align_multiply(s_buf + off, p_buf + ((size_t)unit_size * j + off), len, factor[source_num * j]); +#ifdef TIMER +loop_count2a++; +#endif + } +#ifdef TIMER +time_encode2a += GetTickCount() - time_start2; +#endif + } else { // 全ての消失ブロックを保持する場合 + // スレッドごとに復元する消失ブロックの chunk を変える + src_num = th->size; + while ((j = InterlockedIncrement(&(th->now))) < max_num){ // j = ++th_now + off = j / block_lost; // chunk の番号 + j = j % block_lost; // lost block の番号 + off *= chunk_size; // chunk の位置 + if (off + len > unit_size) + len = unit_size - off; // 最後の chunk だけサイズが異なるかも + work_buf = p_buf + (size_t)unit_size * j + off; + factor2 = factor + source_num * j; + + // ソース・ブロックごとにパリティを追加していく + for (i = 0; i < src_num; i++) + galois_align_multiply(s_buf + ((size_t)unit_size * i + off), work_buf, len, factor2[i]); +#ifdef TIMER +loop_count2b += src_num; +#endif + } +#ifdef TIMER +time_encode2b += GetTickCount() - time_start2; +#endif + } + //_mm_sfence(); // メモリーへの書き込みを完了する + SetEvent(hEnd); // 計算終了を通知する + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + } +#ifdef TIMER +loop_count2a /= chunk_num; // chunk数で割ってブロック数にする +loop_count2b /= chunk_num; +printf("sub-thread : total loop = %d\n", loop_count2a + loop_count2b); +if (time_encode2a > 0){ + i = (int)((__int64)loop_count2a * unit_size * 125 / ((__int64)time_encode2a * 131072)); +} else { + i = 0; +} +if (loop_count2a > 0) + printf(" 1st decode %d.%03d sec, %d loop, %d MB/s\n", time_encode2a / 1000, time_encode2a % 1000, loop_count2a, i); +if (time_encode2b > 0){ + i = (int)((__int64)loop_count2b * unit_size * 125 / ((__int64)time_encode2b * 131072)); +} else { + i = 0; +} +printf(" 2nd decode %d.%03d sec, %d loop, %d MB/s\n", time_encode2b / 1000, time_encode2b % 1000, loop_count2b, i); +#endif + + // 終了処理 + CloseHandle(hRun); + CloseHandle(hEnd); + return 0; +} + +// ブロックごとに計算するためのスレッド +static DWORD WINAPI thread_decode_each(LPVOID lpParameter) +{ + unsigned char *s_buf, *p_buf, *work_buf; + unsigned short *factor, *factor2; + int i, j, th_id, block_lost, src_start, src_num, max_num; + unsigned int unit_size, len, off, chunk_size; + HANDLE hRun, hEnd; + RS_TH *th; +#ifdef TIMER +unsigned int loop_count2a = 0, loop_count2b = 0; +unsigned int time_start2, time_encode2a = 0, time_encode2b = 0; +#endif + + th = (RS_TH *)lpParameter; + p_buf = th->buf; + unit_size = th->size; + chunk_size = th->off; + block_lost = th->count; + th_id = th->now; // スレッド番号 + hRun = th->run; + hEnd = th->end; + //_mm_sfence(); + SetEvent(hEnd); // 設定完了を通知する + + max_num = ((unit_size + chunk_size - 1) / chunk_size) * block_lost; + + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + while (th->now < INT_MAX / 2){ +#ifdef TIMER +time_start2 = GetTickCount(); +#endif + s_buf = th->buf; + factor = th->mat; + + if (th->size == 0xFFFFFFFF){ // ソース・ブロック読み込み中 + src_start = th->off; // ソース・ブロック番号 + len = chunk_size; + // パリティ・ブロックごとに掛け算して追加していく + while ((j = InterlockedIncrement(&(th->now))) < max_num){ // j = ++th_now + off = j / block_lost; // chunk の番号 + j = j % block_lost; // lost block の番号 + off *= chunk_size; + if (off + len > unit_size) + len = unit_size - off; // 最後の chunk だけサイズが異なるかも + if (src_start == 0) // 最初のブロックを計算する際に + memset(p_buf + ((size_t)unit_size * j + off), 0, len); // ブロックを 0で埋める + galois_align_multiply(s_buf + off, p_buf + ((size_t)unit_size * j + off), len, factor[source_num * j]); +#ifdef TIMER +loop_count2a++; +#endif + } +#ifdef TIMER +time_encode2a += GetTickCount() - time_start2; +#endif + } else { + // スレッドごとに復元する消失ブロックを変える + src_num = th->count; + while ((j = InterlockedIncrement(&(th->now))) < block_lost){ // j = ++th_now + work_buf = p_buf + (size_t)unit_size * j; + factor2 = factor + source_num * j; + + // chunk に分割して計算する + len = chunk_size; + off = 0; + while (off < unit_size){ + // ソース・ブロックごとにパリティを追加していく + for (i = 0; i < src_num; i++) + galois_align_multiply(s_buf + ((size_t)unit_size * i + off), work_buf, len, factor2[i]); + + work_buf += len; + off += len; + if (off + len > unit_size) + len = unit_size - off; + } +#ifdef TIMER +loop_count2b += src_num; +#endif + } +#ifdef TIMER +time_encode2b += GetTickCount() - time_start2; +#endif + } + //_mm_sfence(); // メモリーへの書き込みを完了する + SetEvent(hEnd); // 計算終了を通知する + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + } +#ifdef TIMER +loop_count2a /= (unit_size + chunk_size - 1) / chunk_size; // chunk数で割ってブロック数にする +printf("sub-thread[%d] : total loop = %d\n", th_id, loop_count2a + loop_count2b); +if (time_encode2a > 0){ + i = (int)((__int64)loop_count2a * unit_size * 125 / ((__int64)time_encode2a * 131072)); +} else { + i = 0; +} +if (loop_count2a > 0) + printf(" 1st decode %d.%03d sec, %d loop, %d MB/s\n", time_encode2a / 1000, time_encode2a % 1000, loop_count2a, i); +if (time_encode2b > 0){ + i = (int)((__int64)loop_count2b * unit_size * 125 / ((__int64)time_encode2b * 131072)); +} else { + i = 0; +} +printf(" 2nd decode %d.%03d sec, %d loop, %d MB/s\n", time_encode2b / 1000, time_encode2b % 1000, loop_count2b, i); +#endif + + // 終了処理 + CloseHandle(hRun); + CloseHandle(hEnd); + return 0; +} + +// GPU 対応のサブ・スレッド (スレッド番号は最後になる) +static DWORD WINAPI thread_decode_gpu(LPVOID lpParameter) +{ + unsigned char *s_buf, *p_buf; + unsigned short *factor; + int i, j, block_lost, src_num; + unsigned int unit_size; + HANDLE hRun, hEnd; + RS_TH *th; +#ifdef TIMER +unsigned int time_start2, time_encode2 = 0, loop_count2 = 0; +#endif + + th = (RS_TH *)lpParameter; + p_buf = th->buf; + unit_size = th->size; + block_lost = th->count; + hRun = th->run; + hEnd = th->end; + //_mm_sfence(); + SetEvent(hEnd); // 設定完了を通知する + + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + while (th->now < INT_MAX / 2){ +#ifdef TIMER +time_start2 = GetTickCount(); +#endif + // GPUはソース・ブロック読み込み中に呼ばれない + s_buf = th->buf; + factor = th->mat; + src_num = th->count; + + // 最初にソース・ブロックをVRAMへ転送する + i = gpu_copy_blocks(s_buf, unit_size, src_num); + if (i != 0){ + th->size = i; + InterlockedExchange(&(th->now), INT_MAX / 2); // サブ・スレッドの計算を中断する + } + + // スレッドごとに復元する消失ブロックを変える + while ((j = InterlockedIncrement(&(th->now))) < block_lost){ // j = ++th_now + // 倍率は逆行列から部分的にコピーする + i = gpu_multiply_blocks(src_num, factor + source_num * j, p_buf + (size_t)unit_size * j, unit_size); + if (i != 0){ + th->size = i; + break; + } +#ifdef TIMER +loop_count2 += src_num; +#endif + } +#ifdef TIMER +time_encode2 += GetTickCount() - time_start2; +#endif + // 最後にVRAMを解放する + th->size = gpu_finish(); + + //_mm_sfence(); // メモリーへの書き込みを完了する + SetEvent(hEnd); // 計算終了を通知する + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + } +#ifdef TIMER +printf("gpu-thread : total loop = %d\n", loop_count2); +if (time_encode2 > 0){ + i = (int)((__int64)loop_count2 * unit_size * 125 / ((__int64)time_encode2 * 131072)); +} else { + i = 0; +} +printf(" 2nd encode %d.%03d sec, %d loop, %d MB/s\n", time_encode2 / 1000, time_encode2 % 1000, loop_count2, i); +#endif + + // 終了処理 + CloseHandle(hRun); + CloseHandle(hEnd); + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int decode_method1( // ソース・ブロックが一個だけの場合 + wchar_t *file_path, + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_r *files, // ソース・ファイルの情報 + source_ctx_r *s_blk, // ソース・ブロックの情報 + parity_ctx_r *p_blk) // パリティ・ブロックの情報 +{ + unsigned char *buf = NULL, *work_buf, *hash; + int err = 0, id; + unsigned int io_size, unit_size, len, block_off; + unsigned int time_last, prog_num = 0, prog_base; + __int64 file_off; + HANDLE hFile = NULL; + + // 作業バッファーを確保する + len = 0; + io_size = get_io_size(2, &len, 1, sse_unit); + //io_size = (((io_size + 2) / 3 + HASH_SIZE + (sse_unit - 1)) & ~(sse_unit - 1)) - HASH_SIZE; // 実験用 + unit_size = io_size + HASH_SIZE; // チェックサムの分だけ増やす + len = 2 * unit_size + HASH_SIZE; + buf = _aligned_malloc(len, sse_unit); + if (buf == NULL){ + printf("malloc, %d\n", len); + err = 1; + goto error_end; + } + work_buf = buf + unit_size; + hash = work_buf + unit_size; + prog_base = (block_size + io_size - 1) / io_size; // 断片の個数 +#ifdef TIMER + printf("\n read one block, and keep one recovering block\n"); + printf("buffer size = %d MB, io_size = %d, split = %d\n", len >> 20, io_size, (block_size + io_size - 1) / io_size); + id = try_cache_blocking(unit_size); + printf("cache: limit size = %d, chunk_size = %d, split = %d\n", cpu_cache & 0x7FFF8000, id, (unit_size + id - 1) / id); +#endif + + // 書き込み先のファイルを開く + wcscpy(file_path, base_dir); + id = s_blk[0].file; // ファイル番号 + if (files[id].state & 4){ // 破損ファイルを上書きして復元する場合 + // 上書き用のソース・ファイルを開く + hFile = handle_write_file(list_buf + files[id].name, file_path, files[id].size); + } else { + // 作業用のテンポラリ・ファイルを開く + hFile = handle_temp_file(list_buf + files[id].name, file_path); + } + if (hFile == INVALID_HANDLE_VALUE){ + hFile = NULL; + err = 1; + goto error_end; + } + + // 何番のパリティ・ブロックを使うか + for (id = 0; id < parity_num; id++){ + if (p_blk[id].exist == 1) + break; + } + //printf("parity_num = %d, id = %d\n", parity_num, id); + + // バッファー・サイズごとにソース・ブロックを復元する + print_progress_text(0, "Recovering slice"); + time_last = GetTickCount(); + block_off = 0; + while (block_off < block_size){ +#ifdef TIMER +time_start = GetTickCount(); +#endif + // パリティ・ブロックを読み込む + len = block_size - block_off; + if (len > io_size) + len = io_size; + file_off = p_blk[id].off + (__int64)block_off; + if (file_read_data(rcv_hFile[p_blk[id].file], file_off, buf, len)){ + printf("file_read_data, recovery slice %d\n", id); + err = 1; + goto error_end; + } + if (len < io_size) + memset(buf + len, 0, io_size - len); + // パリティ・ブロックのチェックサムを計算する + checksum16_altmap(buf, buf + io_size, io_size); +#ifdef TIMER +time_read += GetTickCount() - time_start; +#endif + +#ifdef TIMER +time_start = GetTickCount(); +#endif + // 失われたソース・ブロックを復元する + memset(work_buf, 0, unit_size); + // factor で割ると元に戻る + galois_align_multiply(buf, work_buf, unit_size, galois_divide(1, galois_power(2, id))); +#ifdef TIMER +time_calc += GetTickCount() - time_start; +#endif + + // 経過表示 + prog_num++; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((prog_num * 1000) / prog_base)){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + +#ifdef TIMER +time_start = GetTickCount(); +#endif + // 復元されたソース・ブロックのチェックサムを検証する + checksum16_return(work_buf, hash, io_size); + if (memcmp(work_buf + io_size, hash, HASH_SIZE) != 0){ + printf("checksum mismatch, recovered input slice %d\n", 0); + err = 1; + goto error_end; + } + // ファイルにソース・ブロックを書き込む + len = s_blk[0].size - block_off; + if (len > io_size) + len = io_size; + if (file_write_data(hFile, (__int64)block_off, work_buf, len)){ + printf("file_write_data, input slice %d\n", 0); + err = 1; + goto error_end; + } +#ifdef TIMER +time_write += GetTickCount() - time_start; +#endif + + block_off += io_size; + } + print_progress_done(); // 末尾ブロックの断片化によっては 100% で完了するとは限らない + +#ifdef TIMER +printf("read %d.%03d sec\n", time_read / 1000, time_read % 1000); +printf("write %d.%03d sec\n", time_write / 1000, time_write % 1000); +printf("decode %d.%03d sec\n", time_calc / 1000, time_calc % 1000); +#endif + +error_end: + if (hFile) + CloseHandle(hFile); + if (buf) + _aligned_free(buf); + return err; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int decode_method2( // ソース・データを全て読み込む場合 + wchar_t *file_path, + int block_lost, // 失われたソース・ブロックの数 + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_r *files, // ソース・ファイルの情報 + source_ctx_r *s_blk, // ソース・ブロックの情報 + parity_ctx_r *p_blk, // パリティ・ブロックの情報 + unsigned short *mat) +{ + unsigned char *buf = NULL, *p_buf, *work_buf, *hash; + unsigned short *id; + int err = 0, i, j, last_file, part_start, part_num, recv_now; + int src_num, chunk_num, cover_num; + unsigned int io_size, unit_size, len, block_off; + unsigned int time_last, prog_write; + __int64 file_off, prog_num = 0, prog_base; + HANDLE hFile = NULL; + HANDLE hSub[MAX_CPU], hRun[MAX_CPU], hEnd[MAX_CPU]; + RS_TH th[1]; + + memset(hSub, 0, sizeof(HANDLE) * MAX_CPU); + id = mat + (block_lost * source_num); // 何番目の消失ソース・ブロックがどのパリティで代替されるか + + // 作業バッファーを確保する + part_num = source_num >> PART_MAX_RATE; // ソース・ブロック数に対する割合で最大量を決める + //part_num = (block_lost + 2) / 3; // 確保量の実験用 + if (part_num < block_lost){ // 分割して計算するなら + i = (block_lost + part_num - 1) / part_num; // 分割回数 + part_num = (block_lost + i - 1) / i; + part_num = ((part_num + cpu_num - 1) / cpu_num) * cpu_num; // cpu_num の倍数にする(切り上げ) + } + if (part_num > block_lost) + part_num = block_lost; + io_size = get_io_size(source_num, &part_num, 1, sse_unit); + //io_size = (((io_size + 1) / 2 + HASH_SIZE + (sse_unit - 1)) & ~(sse_unit - 1)) - HASH_SIZE; // 2分割の実験用 + //io_size = (((io_size + 2) / 3 + HASH_SIZE + (sse_unit - 1)) & ~(sse_unit - 1)) - HASH_SIZE; // 3分割の実験用 + unit_size = io_size + HASH_SIZE; // チェックサムの分だけ増やす + file_off = (source_num + part_num) * (size_t)unit_size + HASH_SIZE; + buf = _aligned_malloc((size_t)file_off, sse_unit); + if (buf == NULL){ + printf("malloc, %I64d\n", file_off); + err = 1; + goto error_end; + } + p_buf = buf + (size_t)unit_size * source_num; // 復元したブロックを記録する領域 + hash = p_buf + (size_t)unit_size * part_num; + prog_base = (block_size + io_size - 1) / io_size; + prog_write = source_num >> 5; // 計算で 97%、書き込みで 3% ぐらい + if (prog_write == 0) + prog_write = 1; + prog_base *= (__int64)(source_num + prog_write) * block_lost; // 全体の断片の個数 + len = try_cache_blocking(unit_size); + //len = ((len + 2) / 3 + (sse_unit - 1)) & ~(sse_unit - 1); // 1/3の実験用 + chunk_num = (unit_size + len - 1) / len; +#ifdef TIMER + printf("\n read all blocks, and keep some recovering blocks\n"); + printf("buffer size = %I64d MB, io_size = %d, split = %d\n", file_off >> 20, io_size, (block_size + io_size - 1) / io_size); + printf("cache: limit size = %d, chunk_size = %d, split = %d\n", cpu_cache & 0x7FFF8000, len, chunk_num); + printf("prog_base = %I64d, unit_size = %d\n", prog_base, unit_size); +#endif + + // マルチ・スレッドの準備をする + th->buf = p_buf; + th->size = unit_size; + th->count = part_num; + th->off = len; // キャッシュの最適化を試みる + for (j = 0; j < cpu_num; j++){ // サブ・スレッドごとに + hRun[j] = CreateEvent(NULL, FALSE, FALSE, NULL); // Auto Reset にする + if (hRun[j] == NULL){ + print_win32_err(); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + hEnd[j] = CreateEvent(NULL, TRUE, FALSE, NULL); + if (hEnd[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + // サブ・スレッドを起動する + th->run = hRun[j]; + th->end = hEnd[j]; + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する + hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_decode2, (LPVOID)th, 0, NULL); + if (hSub[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + CloseHandle(hEnd[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + WaitForSingleObject(hEnd[j], INFINITE); // 設定終了の合図を待つ (リセットしない) + } + // IO が延滞しないように、サブ・スレッド一つの優先度を下げる + SetThreadPriority(hSub[0], THREAD_PRIORITY_BELOW_NORMAL); + + // ブロック断片を読み込んで、消失ブロック断片を復元する + print_progress_text(0, "Recovering slice"); + time_last = GetTickCount(); + wcscpy(file_path, base_dir); + block_off = 0; + while (block_off < block_size){ + th->size = 0; // 1st decode + th->off = -1; // まだ計算して無い印 + +#ifdef TIMER +read_count = 0; +skip_count = 0; +time_start = GetTickCount(); +#endif + last_file = -1; + recv_now = 0; // 何番目の代替ブロックか + for (i = 0; i < source_num; i++){ + switch(s_blk[i].exist){ + case 0: // バッファーにパリティ・ブロックの内容を読み込む + len = block_size - block_off; + if (len > io_size) + len = io_size; + file_off = p_blk[id[recv_now]].off + (__int64)block_off; + if (file_read_data(rcv_hFile[p_blk[id[recv_now]].file], file_off, buf + (size_t)unit_size * i, len)){ + printf("file_read_data, recovery slice %d\n", id[recv_now]); + err = 1; + goto error_end; + } + if (len < io_size) + memset(buf + ((size_t)unit_size * i + len), 0, io_size - len); + recv_now++; + // パリティ・ブロックのチェックサムを計算する + checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + io_size), io_size); +#ifdef TIMER +read_count++; +#endif + break; + case 3: // ソース・ブロックの内容は全て 0 + len = 0; + memset(buf + (size_t)unit_size * i, 0, unit_size); + break; + default: // バッファーにソース・ブロックの内容を読み込む + if (s_blk[i].file != last_file){ // 別のファイルなら開く + last_file = s_blk[i].file; + if (hFile){ + CloseHandle(hFile); // 前のファイルを閉じる + hFile = NULL; + } + if (files[last_file].state & 4){ // 上書き中の破損ファイルから読み込む + wcscpy(file_path + base_len, list_buf + files[last_file].name); + } else if (files[last_file].state & 3){ // 作り直した作業ファイルから読み込む + get_temp_name(list_buf + files[last_file].name, file_path + base_len); + } else if (files[last_file].state & 32){ // 名前訂正失敗時には別名ファイルから読み込む + wcscpy(file_path + base_len, list_buf + files[last_file].name2); + } else { // 完全なソース・ファイルから読み込む + wcscpy(file_path + base_len, list_buf + files[last_file].name); + } + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + hFile = NULL; + printf_cp("cannot open file, %s\n", file_path); + err = 1; + goto error_end; + } + } + if (s_blk[i].size > block_off){ + len = s_blk[i].size - block_off; + if (len > io_size) + len = io_size; + file_off = (i - files[last_file].b_off) * (__int64)block_size + (__int64)block_off; + if (file_read_data(hFile, file_off, buf + (size_t)unit_size * i, len)){ + printf("file_read_data, input slice %d\n", i); + err = 1; + goto error_end; + } + if (len < io_size) + memset(buf + ((size_t)unit_size * i + len), 0, io_size - len); + // ソース・ブロックのチェックサムを計算する + checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + io_size), io_size); +#ifdef TIMER +read_count++; +#endif + } else { + len = 0; + memset(buf + (size_t)unit_size * i, 0, unit_size); + } + } + + if ((len > 0) && (i + 1 < source_num)){ // 最後のブロック以外なら + // サブ・スレッドの動作状況を調べる + j = WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, 0); + if ((j != WAIT_TIMEOUT) && (j != WAIT_FAILED)){ // 計算中でないなら + // 経過表示 + prog_num += part_num; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + // 計算終了したブロックの次から計算を開始する + th->off += 1; + if (th->off > 0){ // バッファーに読み込んだ時だけ計算する + while ((s_blk[th->off].exist != 0) && + ((s_blk[th->off].size <= block_off) || (s_blk[th->off].exist == 3))){ + prog_num += part_num; + th->off += 1; +#ifdef TIMER +skip_count++; +#endif + } + } + th->buf = buf + (size_t)unit_size * th->off; + th->mat = mat + th->off; + th->now = -1; // 初期値 - 1 + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを再開する + for (j = 0; j < (cpu_num + 1) / 2; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + } + } + } + if (hFile){ // 最後の読み込みファイルを閉じる + CloseHandle(hFile); + hFile = NULL; + } +#ifdef TIMER +time_read += GetTickCount() - time_start; +#endif + + WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, INFINITE); // サブ・スレッドの計算終了の合図を待つ + th->off += 1; // 計算を開始するソース・ブロックの番号 + if (th->off > 0){ // 計算不要なソース・ブロックはとばす + while ((s_blk[th->off].exist != 0) && + ((s_blk[th->off].size <= block_off) || (s_blk[th->off].exist == 3))){ + prog_num += part_num; + th->off += 1; +#ifdef TIMER +skip_count++; +#endif + } + } else { // エラーや実験時以外は th->off は 0 にならない + memset(p_buf, 0, (size_t)unit_size * part_num); + } +#ifdef TIMER + j = (th->off * 1000) / source_num; + printf("partial decode = %d (%d.%d%%), read = %d, skip = %d\n", th->off, j / 10, j % 10, read_count, skip_count); +#endif + recv_now = -1; // 消失ブロックの本来のソース番号 + last_file = -1; + + // cover_num ごとに処理する + part_start = 0; + cover_num = part_num; // part_num は cpu_num の倍数にすること + src_num = source_num - th->off; // 一度に処理する量 (src_num > 0) + th->buf = buf + (size_t)unit_size * (th->off); + while (part_start < block_lost){ + if (part_start == part_num){ // part_num 分の計算が終わったら + th->off = 0; // 最初の計算以降は全てのソース・ブロックを対象にする + src_num = source_num; // source_num - th->off + th->buf = buf; // buf + (size_t)unit_size * (th->off); + } + if (part_start + cover_num > block_lost) + cover_num = block_lost - part_start; + //printf("part_start = %d, src_num = %d / %d, cover_num = %d\n", part_start, src_num, source_num, cover_num); + + // スレッドごとに消失ブロックを計算する + th->mat = mat + (th->off); + th->size = cover_num; + th->count = part_start; + th->now = -1; // 初期値 - 1 + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを再開する + for (j = 0; j < cpu_num; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + + // サブ・スレッドの計算終了の合図を UPDATE_TIME だけ待つ + while (WaitForMultipleObjects(cpu_num, hEnd, TRUE, UPDATE_TIME) == WAIT_TIMEOUT){ + // th-now が最高値なので、計算が終わってるのは th-now - cpu_num 個となる + j = th->now - cpu_num; + if (j < 0) + j = 0; + j /= chunk_num; // chunk数で割ってブロック数にする + // 経過表示(UPDATE_TIME 時間待った場合なので、必ず経過してるはず) + if (print_progress((int)(((prog_num + src_num * j) * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + prog_num += src_num * cover_num; + +#ifdef TIMER +time_start = GetTickCount(); +#endif + // 復元されたブロックを書き込む + work_buf = p_buf; + for (i = part_start; i < part_start + cover_num; i++){ + for (j = recv_now + 1; j < source_num; j++){ // 何番のソース・ブロックか + if (s_blk[j].exist == 0){ + recv_now = j; + break; + } + } + //printf(" lost block[%d] = source block[%d]\n", i, recv_now); + + // 復元されたソース・ブロックのチェックサムを検証する + checksum16_return(work_buf, hash, io_size); + if (memcmp(work_buf + io_size, hash, HASH_SIZE) != 0){ + printf("checksum mismatch, recovered input slice %d\n", recv_now); + err = 1; + goto error_end; + } + if (s_blk[recv_now].size <= block_off){ // 書き込み不要 + work_buf += unit_size; + prog_num += prog_write; + continue; + } + // ファイルにソース・ブロックを書き込む + if (s_blk[recv_now].file != last_file){ // 別のファイルなら開く + last_file = s_blk[recv_now].file; + if (hFile){ + CloseHandle(hFile); // 前のファイルを閉じる + hFile = NULL; + } + if (files[last_file].state & 4){ // 破損ファイルを上書きして復元する場合 + // 上書き用のソース・ファイルを開く + hFile = handle_write_file(list_buf + files[last_file].name, file_path, files[last_file].size); + } else { + // 作業ファイルを開く + hFile = handle_temp_file(list_buf + files[last_file].name, file_path); + } + if (hFile == INVALID_HANDLE_VALUE){ + hFile = NULL; + err = 1; + goto error_end; + } + //printf("file %d, open %S\n", last_file, file_path); + } + // ソース・ファイル内でのブロック断片の大きさと位置 + len = s_blk[recv_now].size - block_off; + if (len > io_size) + len = io_size; + if (file_write_data(hFile, (recv_now - files[last_file].b_off) * (__int64)block_size + block_off, work_buf, len)){ + printf("file_write_data, input slice %d\n", recv_now); + err = 1; + goto error_end; + } +#ifdef TIMER +write_count++; +#endif + work_buf += unit_size; + + // 経過表示 + prog_num += prog_write; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + } +#ifdef TIMER +time_write += GetTickCount() - time_start; +#endif + + part_start += part_num; // 次の消失ブロック位置にする + } + + block_off += io_size; + // 最後の書き込みファイルを閉じる + CloseHandle(hFile); + hFile = NULL; + } + print_progress_done(); + //printf("prog_num = %I64d / %I64d\n", prog_num, prog_base); + +#ifdef TIMER +printf("read %d.%03d sec\n", time_read / 1000, time_read % 1000); +j = ((block_size + io_size - 1) / io_size) * block_lost; +printf("write %d.%03d sec, count = %d/%d\n", time_write / 1000, time_write % 1000, write_count, j); +#endif + +error_end: + InterlockedExchange(&(th->now), INT_MAX / 2); // サブ・スレッドの計算を中断する + for (j = 0; j < cpu_num; j++){ + if (hSub[j]){ // サブ・スレッドを終了させる + SetEvent(hRun[j]); + WaitForSingleObject(hSub[j], INFINITE); + CloseHandle(hSub[j]); + } + } + if (hFile) + CloseHandle(hFile); + if (buf) + _aligned_free(buf); + return err; +} + +int decode_method3( // 復元するブロックを全て保持できる場合 + wchar_t *file_path, + int block_lost, // 失われたソース・ブロックの数 + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_r *files, // ソース・ファイルの情報 + source_ctx_r *s_blk, // ソース・ブロックの情報 + parity_ctx_r *p_blk, // パリティ・ブロックの情報 + unsigned short *mat) +{ + unsigned char *buf = NULL, *p_buf, *work_buf, *hash; + unsigned short *id; + int err = 0, i, j, last_file, source_off, read_num, recv_now, parity_now; + int src_num, chunk_num; + unsigned int unit_size, len; + unsigned int time_last, prog_write; + __int64 file_off, prog_num = 0, prog_base; + HANDLE hFile = NULL; + HANDLE hSub[MAX_CPU], hRun[MAX_CPU], hEnd[MAX_CPU]; + RS_TH th[1]; + + memset(hSub, 0, sizeof(HANDLE) * MAX_CPU); + id = mat + (block_lost * source_num); // 何番目の消失ソース・ブロックがどのパリティで代替されるか + unit_size = (block_size + HASH_SIZE + (sse_unit - 1)) & ~(sse_unit - 1); // チェックサムの分だけ増やす + + // 作業バッファーを確保する + read_num = read_block_num(block_lost, 0, 1, sse_unit); // ソース・ブロックを何個読み込むか + if (read_num == 0){ + //printf("cannot keep enough blocks, use another method\n"); + return -2; // スライスを分割して処理しないと無理 + } + //read_num = (read_num + 1) / 2 + 1; // 2分割の実験用 + //read_num = (read_num + 2) / 3 + 1; // 3分割の実験用 + file_off = (read_num + block_lost) * (size_t)unit_size + HASH_SIZE; + buf = _aligned_malloc((size_t)file_off, sse_unit); + if (buf == NULL){ + printf("malloc, %I64d\n", file_off); + err = 1; + goto error_end; + } + p_buf = buf + (size_t)unit_size * read_num; // パリティ・ブロックを記録する領域 + hash = p_buf + (size_t)unit_size * block_lost; + prog_write = source_num >> 5; // 計算で 97%、書き込みで 3% ぐらい + if (prog_write == 0) + prog_write = 1; + prog_base = (__int64)(source_num + prog_write) * block_lost; // ブロックの合計掛け算個数 + 書き込み回数 + len = try_cache_blocking(unit_size); + chunk_num = (unit_size + len - 1) / len; +#ifdef TIMER + printf("\n read some blocks, and keep all recovering blocks\n"); + printf("buffer size = %I64d MB, read_num = %d, round = %d\n", file_off >> 20, read_num, (source_num + read_num - 1) / read_num); + printf("cache: limit size = %d, chunk_size = %d, split = %d\n", cpu_cache & 0x7FFF8000, len, chunk_num); + printf("prog_base = %I64d, unit_size = %d\n", prog_base, unit_size); +#endif + + // マルチ・スレッドの準備をする + th->buf = p_buf; + th->size = unit_size; + th->count = block_lost; + th->off = len; // キャッシュの最適化を試みる + for (j = 0; j < cpu_num; j++){ // サブ・スレッドごとに + hRun[j] = CreateEvent(NULL, FALSE, FALSE, NULL); // Auto Reset にする + if (hRun[j] == NULL){ + print_win32_err(); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + hEnd[j] = CreateEvent(NULL, TRUE, FALSE, NULL); + if (hEnd[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + // サブ・スレッドを起動する + th->run = hRun[j]; + th->end = hEnd[j]; + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する + hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_decode3, (LPVOID)th, 0, NULL); + if (hSub[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + CloseHandle(hEnd[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + WaitForSingleObject(hEnd[j], INFINITE); // 設定終了の合図を待つ (リセットしない) + } + // IO が延滞しないように、サブ・スレッド一つの優先度を下げる + SetThreadPriority(hSub[0], THREAD_PRIORITY_BELOW_NORMAL); + + // 何回かに別けてブロックを読み込んで、消失ブロックを少しずつ復元する + print_progress_text(0, "Recovering slice"); + time_last = GetTickCount(); + wcscpy(file_path, base_dir); + parity_now = 0; // 何番目の代替ブロックか + source_off = 0; // 読み込み開始スライス番号 + while (source_off < source_num){ + if (read_num > source_num - source_off) + read_num = source_num - source_off; + th->size = 0; // 1st decode + th->off = source_off - 1; // まだ計算して無い印 + +#ifdef TIMER +read_count = 0; +time_start = GetTickCount(); +#endif + last_file = -1; + for (i = 0; i < read_num; i++){ // スライスを一個ずつ読み込んでメモリー上に配置していく + switch(s_blk[source_off + i].exist){ + case 0: // バッファーにパリティ・ブロックの内容を読み込む + if (file_read_data(rcv_hFile[p_blk[id[parity_now]].file], p_blk[id[parity_now]].off, buf + (size_t)unit_size * i, block_size)){ + printf("file_read_data, recovery slice %d\n", id[parity_now]); + err = 1; + goto error_end; + } + parity_now++; + // パリティ・ブロックのチェックサムを計算する + checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + unit_size - HASH_SIZE), unit_size - HASH_SIZE); +#ifdef TIMER +read_count++; +#endif + break; + case 3: // ソース・ブロックの内容は全て 0 + memset(buf + (size_t)unit_size * i, 0, unit_size); + break; + default: // バッファーにソース・ブロックの内容を読み込む + if (s_blk[source_off + i].file != last_file){ // 別のファイルなら開く + last_file = s_blk[source_off + i].file; + if (hFile){ + CloseHandle(hFile); // 前のファイルを閉じる + hFile = NULL; + } + if (files[last_file].state & 4){ // 上書き中の破損ファイルから読み込む + wcscpy(file_path + base_len, list_buf + files[last_file].name); + } else if (files[last_file].state & 3){ // 作り直した作業ファイルから読み込む + get_temp_name(list_buf + files[last_file].name, file_path + base_len); + } else if (files[last_file].state & 32){ // 名前訂正失敗時には別名ファイルから読み込む + wcscpy(file_path + base_len, list_buf + files[last_file].name2); + } else { // 完全なソース・ファイルから読み込む (追加訂正失敗時も) + wcscpy(file_path + base_len, list_buf + files[last_file].name); + } + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + hFile = NULL; + printf_cp("cannot open file, %s\n", file_path); + err = 1; + goto error_end; + } + } + len = s_blk[source_off + i].size; + file_off = (source_off + i - files[last_file].b_off) * (__int64)block_size; + if (file_read_data(hFile, file_off, buf + (size_t)unit_size * i, len)){ + printf("file_read_data, input slice %d\n", source_off + i); + err = 1; + goto error_end; + } + if (len < block_size) + memset(buf + ((size_t)unit_size * i + len), 0, block_size - len); + // ソース・ブロックのチェックサムを計算する + checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + unit_size - HASH_SIZE), unit_size - HASH_SIZE); +#ifdef TIMER +read_count++; +#endif + } + + if (i + 1 < read_num){ // 最後のブロック以外なら + // サブ・スレッドの動作状況を調べる + j = WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, 0); + if ((j != WAIT_TIMEOUT) && (j != WAIT_FAILED)){ // 計算中でないなら + // 経過表示 + prog_num += block_lost; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + // 計算終了したブロックの次から計算を開始する + th->off += 1; + th->buf = buf + (size_t)unit_size * (th->off - source_off); + th->mat = mat + th->off; + th->now = -1; // 初期値 - 1 + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを再開する + for (j = 0; j < (cpu_num + 1) / 2; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + } + } + } + if (hFile){ // 最後の読み込みファイルを閉じる + CloseHandle(hFile); + hFile = NULL; + } +#ifdef TIMER +time_read += GetTickCount() - time_start; +#endif + + WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, INFINITE); // サブ・スレッドの計算終了の合図を待つ + th->off += 1; // 計算を開始するソース・ブロックの番号 + if (th->off == 0) // エラーや実験時以外は th->off は 0 にならない + memset(p_buf, 0, (size_t)unit_size * block_lost); +#ifdef TIMER + j = (th->off - source_off) * 1000 / read_num; + printf("partial decode = %d (%d.%d%%), read = %d\n", th->off - source_off, j / 10, j % 10, read_count); +#endif + recv_now = -1; // 消失ブロックの本来のソース番号 + last_file = -1; + + // スレッドごとに消失ブロックを計算する + src_num = read_num - (th->off - source_off); // 一度に処理する量 (src_num > 0) + th->buf = buf + (size_t)unit_size * (th->off - source_off); + th->mat = mat + th->off; + // th->off はソース・ブロックの番号 + th->size = src_num; + th->now = -1; // 初期値 - 1 + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを再開する + for (j = 0; j < cpu_num; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + + // サブ・スレッドの計算終了の合図を UPDATE_TIME だけ待つ + while (WaitForMultipleObjects(cpu_num, hEnd, TRUE, UPDATE_TIME) == WAIT_TIMEOUT){ + // th-now が最高値なので、計算が終わってるのは th-now - cpu_num 個となる + j = th->now - cpu_num; + if (j < 0) + j = 0; + j /= chunk_num; // chunk数で割ってブロック数にする + // 経過表示(UPDATE_TIME 時間待った場合なので、必ず経過してるはず) + if (print_progress((int)(((prog_num + src_num * j) * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + + // 経過表示 + prog_num += src_num * block_lost; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + + source_off += read_num; + } + //printf("\nprog_num = %I64d / %I64d\n", prog_num, prog_base); + +#ifdef TIMER +time_start = GetTickCount(); +#endif + // 復元されたブロックを書き込む + work_buf = p_buf; + for (i = 0; i < block_lost; i++){ + for (j = recv_now + 1; j < source_num; j++){ // 何番のソース・ブロックか + if (s_blk[j].exist == 0){ + recv_now = j; + break; + } + } + //printf(" lost block[%d] = source block[%d]\n", i, recv_now); + + // 復元されたソース・ブロックのチェックサムを検証する + checksum16_return(work_buf, hash, unit_size - HASH_SIZE); + if (memcmp(work_buf + unit_size - HASH_SIZE, hash, HASH_SIZE) != 0){ + printf("checksum mismatch, recovered input slice %d\n", recv_now); + err = 1; + goto error_end; + } + // ファイルにソース・ブロックを書き込む + if (s_blk[recv_now].file != last_file){ // 別のファイルなら開く + last_file = s_blk[recv_now].file; + if (hFile){ + CloseHandle(hFile); // 前のファイルを閉じる + hFile = NULL; + } + if (files[last_file].state & 4){ // 破損ファイルを上書きして復元する場合 + // 上書き用のソース・ファイルを開く + hFile = handle_write_file(list_buf + files[last_file].name, file_path, files[last_file].size); + } else { + // 作業ファイルを開く + hFile = handle_temp_file(list_buf + files[last_file].name, file_path); + } + if (hFile == INVALID_HANDLE_VALUE){ + hFile = NULL; + err = 1; + goto error_end; + } + //printf("file %d, open %S\n", last_file, file_path); + } + if (file_write_data(hFile, (recv_now - files[last_file].b_off) * (__int64)block_size, work_buf, s_blk[recv_now].size)){ + printf("file_write_data, input slice %d\n", recv_now); + err = 1; + goto error_end; + } + work_buf += unit_size; + + // 経過表示 + prog_num += prog_write; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + } +#ifdef TIMER +time_write += GetTickCount() - time_start; +#endif + // 最後の書き込みファイルを閉じる + CloseHandle(hFile); + hFile = NULL; + print_progress_done(); + //printf("prog_num = %I64d / %I64d\n", prog_num, prog_base); + +#ifdef TIMER +printf("read %d.%03d sec\n", time_read / 1000, time_read % 1000); +printf("write %d.%03d sec\n", time_write / 1000, time_write % 1000); +#endif + +error_end: + InterlockedExchange(&(th->now), INT_MAX / 2); // サブ・スレッドの計算を中断する + for (j = 0; j < cpu_num; j++){ + if (hSub[j]){ // サブ・スレッドを終了させる + SetEvent(hRun[j]); + WaitForSingleObject(hSub[j], INFINITE); + CloseHandle(hSub[j]); + } + } + if (hFile) + CloseHandle(hFile); + if (buf) + _aligned_free(buf); + return err; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int decode_method4( // 全てのブロックを断片的に保持する場合 (GPU対応) + wchar_t *file_path, + int block_lost, // 失われたソース・ブロックの数 + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_r *files, // ソース・ファイルの情報 + source_ctx_r *s_blk, // ソース・ブロックの情報 + parity_ctx_r *p_blk, // パリティ・ブロックの情報 + unsigned short *mat) +{ + unsigned char *buf = NULL, *p_buf, *work_buf, *hash; + unsigned short *id; + int err = 0, i, j, last_file, recv_now; + int cpu_num1, cover_max, cover_from, cover_num; + unsigned int io_size, unit_size, len, block_off; + unsigned int time_last, prog_write; + __int64 file_off, prog_num = 0, prog_base; + HANDLE hFile = NULL; + HANDLE hSub[MAX_CPU], hRun[MAX_CPU], hEnd[MAX_CPU]; + RS_TH th[1]; + + memset(hSub, 0, sizeof(HANDLE) * MAX_CPU); + id = mat + (block_lost * source_num); // 何番目の消失ソース・ブロックがどのパリティで代替されるか + cpu_num1 = cpu_num; // 最後のスレッドを GPU 管理用にする + if (cpu_num == 1) + cpu_num1++; + + // 作業バッファーを確保する(GPU の作業領域として2個の余裕を見ておく) + // part_num を使わず、全てのブロックを保持する所がdecode_method2と異なることに注意! + io_size = get_io_size(source_num + block_lost + 2, NULL, 1, MEM_UNIT); + //io_size = (((io_size + 1) / 2 + HASH_SIZE + (MEM_UNIT - 1)) & ~(MEM_UNIT - 1)) - HASH_SIZE; // 2分割の実験用 + //io_size = (((io_size + 2) / 3 + HASH_SIZE + (MEM_UNIT - 1)) & ~(MEM_UNIT - 1)) - HASH_SIZE; // 3分割の実験用 + unit_size = io_size + HASH_SIZE; // チェックサムの分だけ増やす + file_off = (source_num + block_lost) * (size_t)unit_size + HASH_SIZE; + buf = _aligned_malloc((size_t)file_off, MEM_UNIT); // GPU 用の境界 + if (buf == NULL){ + printf("malloc, %I64d\n", file_off); + err = 1; + goto error_end; + } + p_buf = buf + (size_t)unit_size * source_num; // 復元したブロックを記録する領域 + hash = p_buf + (size_t)unit_size * block_lost; + prog_base = (block_size + io_size - 1) / io_size; + prog_write = source_num >> 5; // 計算で 97%、書き込みで 3% ぐらい + if (prog_write == 0) + prog_write = 1; + prog_base *= (__int64)(source_num + prog_write) * block_lost; // 全体の断片の個数 +#ifdef TIMER + printf("\n read all blocks, and keep all recovering blocks (GPU)\n"); + printf("buffer size = %I64d MB, io_size = %d, split = %d\n", file_off >> 20, io_size, (block_size + io_size - 1) / io_size); +#endif + + // OpenCL の初期化 + cover_max = source_num; + len = 0; + i = init_OpenCL(unit_size, &cover_max, &len); + if (i != 0){ + if (i != 3) // GPU が見つからなかった場合はエラー表示しない + printf("init_OpenCL, %d, %d\n", i & 0xFF, i >> 8); + i = free_OpenCL(); + if (i != 0) + printf("free_OpenCL, %d, %d", i & 0xFF, i >> 8); + OpenCL_method = 0; // GPU を使わない設定にする + // GPU を使わずに計算を続行する場合は以下をコメントアウト + err = -2; // CPU だけの方式に切り替える + goto error_end; + } + if (len == 0) // GPUがキャッシュを使わない時だけ、CPU独自にキャッシュの最適化を試みる + len = try_cache_blocking(unit_size); +#ifdef TIMER + printf("cache: limit size = %d, chunk_size = %d, split = %d\n", cpu_cache & 0x7FFF8000, len, (unit_size + len - 1) / len); + printf("prog_base = %I64d, unit_size = %d, method = %d, cover_max = %d\n", prog_base, unit_size, OpenCL_method, cover_max); +#endif + + // マルチ・スレッドの準備をする + th->buf = p_buf; + th->size = unit_size; + th->count = block_lost; + th->off = len; // chunk size + for (j = 0; j < cpu_num1; j++){ // サブ・スレッドごとに + hRun[j] = CreateEvent(NULL, FALSE, FALSE, NULL); // Auto Reset にする + if (hRun[j] == NULL){ + print_win32_err(); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + hEnd[j] = CreateEvent(NULL, TRUE, FALSE, NULL); + if (hEnd[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + // サブ・スレッドを起動する + th->run = hRun[j]; + th->end = hEnd[j]; + th->now = j; // スレッド番号 + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する + if ((j == cpu_num1 - 1) && (OpenCL_method != 0)){ // 最後のスレッドを GPU 管理用にする + hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_decode_gpu, (LPVOID)th, 0, NULL); + } else { + hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_decode_each, (LPVOID)th, 0, NULL); + } + if (hSub[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + CloseHandle(hEnd[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + WaitForSingleObject(hEnd[j], INFINITE); // 設定終了の合図を待つ (リセットしない) + } + // IO が延滞しないように、サブ・スレッド一つの優先度を下げる + SetThreadPriority(hSub[0], THREAD_PRIORITY_BELOW_NORMAL); + + // ブロック断片を読み込んで、消失ブロック断片を復元する + print_progress_text(0, "Recovering slice"); + time_last = GetTickCount(); + wcscpy(file_path, base_dir); + block_off = 0; + while (block_off < block_size){ + th->size = 0xFFFFFFFF; // 1st decode + th->off = -1; // まだ計算して無い印 + +#ifdef TIMER +read_count = 0; +skip_count = 0; +time_start = GetTickCount(); +#endif + last_file = -1; + recv_now = 0; // 何番目の代替ブロックか + for (i = 0; i < source_num; i++){ + switch(s_blk[i].exist){ + case 0: // バッファーにパリティ・ブロックの内容を読み込む + len = block_size - block_off; + if (len > io_size) + len = io_size; + file_off = p_blk[id[recv_now]].off + (__int64)block_off; + if (file_read_data(rcv_hFile[p_blk[id[recv_now]].file], file_off, buf + (size_t)unit_size * i, len)){ + printf("file_read_data, recovery slice %d\n", id[recv_now]); + err = 1; + goto error_end; + } + if (len < io_size) + memset(buf + ((size_t)unit_size * i + len), 0, io_size - len); + recv_now++; + // パリティ・ブロックのチェックサムを計算する + checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + io_size), io_size); +#ifdef TIMER +read_count++; +#endif + break; + case 3: // ソース・ブロックの内容は全て 0 + len = 0; + memset(buf + (size_t)unit_size * i, 0, unit_size); + break; + default: // バッファーにソース・ブロックの内容を読み込む + if (s_blk[i].file != last_file){ // 別のファイルなら開く + last_file = s_blk[i].file; + if (hFile){ + CloseHandle(hFile); // 前のファイルを閉じる + hFile = NULL; + } + if (files[last_file].state & 4){ // 上書き中の破損ファイルから読み込む + wcscpy(file_path + base_len, list_buf + files[last_file].name); + } else if (files[last_file].state & 3){ // 作り直した作業ファイルから読み込む + get_temp_name(list_buf + files[last_file].name, file_path + base_len); + } else if (files[last_file].state & 32){ // 名前訂正失敗時には別名ファイルから読み込む + wcscpy(file_path + base_len, list_buf + files[last_file].name2); + } else { // 完全なソース・ファイルから読み込む + wcscpy(file_path + base_len, list_buf + files[last_file].name); + } + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + hFile = NULL; + printf_cp("cannot open file, %s\n", file_path); + err = 1; + goto error_end; + } + } + if (s_blk[i].size > block_off){ + len = s_blk[i].size - block_off; + if (len > io_size) + len = io_size; + file_off = (i - files[last_file].b_off) * (__int64)block_size + (__int64)block_off; + if (file_read_data(hFile, file_off, buf + (size_t)unit_size * i, len)){ + printf("file_read_data, input slice %d\n", i); + err = 1; + goto error_end; + } + if (len < io_size) + memset(buf + ((size_t)unit_size * i + len), 0, io_size - len); + // ソース・ブロックのチェックサムを計算する + checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + io_size), io_size); +#ifdef TIMER +read_count++; +#endif + } else { + len = 0; + memset(buf + (size_t)unit_size * i, 0, unit_size); + } + } + + if ((len > 0) && (i + 1 < source_num)){ // 最後のブロック以外なら + // サブ・スレッドの動作状況を調べる + j = WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, 0); + if ((j != WAIT_TIMEOUT) && (j != WAIT_FAILED)){ // 計算中でないなら + // 経過表示 + prog_num += block_lost; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + // 計算終了したブロックの次から計算を開始する + th->off += 1; + if (th->off > 0){ // バッファーに読み込んだ時だけ計算する + while ((s_blk[th->off].exist != 0) && + ((s_blk[th->off].size <= block_off) || (s_blk[th->off].exist == 3))){ + prog_num += block_lost; + th->off += 1; +#ifdef TIMER +skip_count++; +#endif + } + } + th->buf = buf + (size_t)unit_size * th->off; + th->mat = mat + th->off; + th->now = -1; // 初期値 - 1 + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを再開する + for (j = 0; j < (cpu_num + 1) / 2; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + } + } + } + if (hFile){ // 最後の読み込みファイルを閉じる + CloseHandle(hFile); + hFile = NULL; + } +#ifdef TIMER +time_read += GetTickCount() - time_start; +#endif + + WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, INFINITE); // サブ・スレッドの計算終了の合図を待つ + th->size = 0; // 2nd decode + th->off += 1; // 計算を開始するソース・ブロックの番号 + if (th->off > 0){ // 計算不要なソース・ブロックはとばす + while ((s_blk[th->off].exist != 0) && + ((s_blk[th->off].size <= block_off) || (s_blk[th->off].exist == 3))){ + prog_num += block_lost; + th->off += 1; +#ifdef TIMER +skip_count++; +#endif + } + } else { // エラーや実験時以外は th->off は 0 にならない + memset(p_buf, 0, (size_t)unit_size * block_lost); + } +#ifdef TIMER + j = (th->off * 1000) / source_num; + printf("partial decode = %d (%d.%d%%), read = %d, skip = %d\n", th->off, j / 10, j % 10, read_count, skip_count); +#endif + recv_now = -1; // 消失ブロックの本来のソース番号 + last_file = -1; + + // VRAM のサイズに応じて分割する + cover_from = th->off; + i = (source_num - cover_from + cover_max - 1) / cover_max; // 何回に分けて処理するか + cover_num = (source_num - cover_from + i - 1) / i; // 一度に処理する量を平均化する + while (cover_from < source_num){ + // ソース・ブロックを何個ずつ処理するか + if (cover_from + cover_num > source_num) + cover_num = source_num - cover_from; + //printf("cover_from = %d, cover_num = %d\n", cover_from, cover_num); + + // GPU と CPU がスレッドごとに消失ブロックを計算する + th->buf = buf + (size_t)unit_size * cover_from; + th->mat = mat + cover_from; + th->off = cover_from; + th->count = cover_num; + th->now = -1; // 初期値 - 1 + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを再開する + for (j = 0; j < cpu_num1; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + + // サブ・スレッドの計算終了の合図を UPDATE_TIME だけ待つ + while (WaitForMultipleObjects(cpu_num1, hEnd, TRUE, UPDATE_TIME) == WAIT_TIMEOUT){ + // th-now が最高値なので、計算が終わってるのは th-now - cpu_num1 個となる + j = th->now - cpu_num1; + if (j < 0) + j = 0; + // 経過表示(UPDATE_TIME 時間待った場合なので、必ず経過してるはず) + if (print_progress((int)(((prog_num + cover_num * j) * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + if (th->size != 0){ // エラー発生 + i = th->size; + printf("error, gpu-thread, %d, %d\n", i & 0xFF, i >> 8); + err = 1; + goto error_end; + } + + // 経過表示 + prog_num += cover_num * block_lost; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + + cover_from += cover_num; + } + +#ifdef TIMER +time_start = GetTickCount(); +#endif + // 復元されたブロックを書き込む + work_buf = p_buf; + for (i = 0; i < block_lost; i++){ + for (j = recv_now + 1; j < source_num; j++){ // 何番のソース・ブロックか + if (s_blk[j].exist == 0){ + recv_now = j; + break; + } + } + //printf(" lost block[%d] = source block[%d]\n", i, recv_now); + + // 復元されたソース・ブロックのチェックサムを検証する + checksum16_return(work_buf, hash, io_size); + if (memcmp(work_buf + io_size, hash, HASH_SIZE) != 0){ + printf("checksum mismatch, recovered input slice %d\n", recv_now); + err = 1; + goto error_end; + } + if (s_blk[recv_now].size <= block_off){ // 書き込み不要 + work_buf += unit_size; + prog_num += prog_write; + continue; + } + // ファイルにソース・ブロックを書き込む + if (s_blk[recv_now].file != last_file){ // 別のファイルなら開く + last_file = s_blk[recv_now].file; + if (hFile){ + CloseHandle(hFile); // 前のファイルを閉じる + hFile = NULL; + } + if (files[last_file].state & 4){ // 破損ファイルを上書きして復元する場合 + // 上書き用のソース・ファイルを開く + hFile = handle_write_file(list_buf + files[last_file].name, file_path, files[last_file].size); + } else { + // 作業ファイルを開く + hFile = handle_temp_file(list_buf + files[last_file].name, file_path); + } + if (hFile == INVALID_HANDLE_VALUE){ + hFile = NULL; + err = 1; + goto error_end; + } + //printf("file %d, open %S\n", last_file, file_path); + } + // ソース・ファイル内でのブロック断片の大きさと位置 + len = s_blk[recv_now].size - block_off; + if (len > io_size) + len = io_size; + if (file_write_data(hFile, (recv_now - files[last_file].b_off) * (__int64)block_size + block_off, work_buf, len)){ + printf("file_write_data, input slice %d\n", recv_now); + err = 1; + goto error_end; + } +#ifdef TIMER +write_count++; +#endif + work_buf += unit_size; + + // 経過表示 + prog_num += prog_write; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + } +#ifdef TIMER +time_write += GetTickCount() - time_start; +#endif + + block_off += io_size; + // 最後の書き込みファイルを閉じる + CloseHandle(hFile); + hFile = NULL; + } + print_progress_done(); + //printf("prog_num = %I64d / %I64d\n", prog_num, prog_base); + +#ifdef TIMER +printf("read %d.%03d sec\n", time_read / 1000, time_read % 1000); +j = ((block_size + io_size - 1) / io_size) * block_lost; +printf("write %d.%03d sec, count = %d/%d\n", time_write / 1000, time_write % 1000, write_count, j); +#endif + info_OpenCL(buf, MEM_UNIT); // デバイス情報を表示する + +error_end: + InterlockedExchange(&(th->now), INT_MAX / 2); // サブ・スレッドの計算を中断する + for (j = 0; j < cpu_num1; j++){ + if (hSub[j]){ // サブ・スレッドを終了させる + SetEvent(hRun[j]); + WaitForSingleObject(hSub[j], INFINITE); + CloseHandle(hSub[j]); + } + } + if (hFile) + CloseHandle(hFile); + if (buf) + _aligned_free(buf); + i = free_OpenCL(); + if (i != 0) + printf("free_OpenCL, %d, %d", i & 0xFF, i >> 8); + return err; +} + +int decode_method5( // 復元するブロックだけ保持する場合 (GPU対応) + wchar_t *file_path, + int block_lost, // 失われたソース・ブロックの数 + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_r *files, // ソース・ファイルの情報 + source_ctx_r *s_blk, // ソース・ブロックの情報 + parity_ctx_r *p_blk, // パリティ・ブロックの情報 + unsigned short *mat) +{ + unsigned char *buf = NULL, *p_buf, *work_buf, *hash; + unsigned short *id; + int err = 0, i, j, last_file, source_off, read_num, recv_now, parity_now; + int cpu_num1, cover_max, cover_from, cover_num; + unsigned int unit_size, len; + unsigned int time_last, prog_write; + __int64 file_off, prog_num = 0, prog_base; + HANDLE hFile = NULL; + HANDLE hSub[MAX_CPU], hRun[MAX_CPU], hEnd[MAX_CPU]; + RS_TH th[1]; + + memset(hSub, 0, sizeof(HANDLE) * MAX_CPU); + id = mat + (block_lost * source_num); // 何番目の消失ソース・ブロックがどのパリティで代替されるか + unit_size = (block_size + HASH_SIZE + (MEM_UNIT - 1)) & ~(MEM_UNIT - 1); // MEM_UNIT の倍数にする + cpu_num1 = cpu_num; // 最後のスレッドを GPU 管理用にする + if (cpu_num == 1) + cpu_num1++; + + // 作業バッファーを確保する(GPU の作業領域として2個の余裕を見ておく) + read_num = read_block_num(block_lost, 2, 1, MEM_UNIT); // ソース・ブロックを何個読み込むか + if (read_num == 0){ + //printf("cannot keep enough blocks, use another method\n"); + return -4; // スライスを分割して処理しないと無理 + } + //read_num = (read_num + 1) / 2 + 1; // 2分割の実験用 + //read_num = (read_num + 2) / 3 + 1; // 3分割の実験用 + file_off = (read_num + block_lost) * (size_t)unit_size + HASH_SIZE; + buf = _aligned_malloc((size_t)file_off, MEM_UNIT); // GPU 用の境界 + if (buf == NULL){ + printf("malloc, %I64d\n", file_off); + err = 1; + goto error_end; + } + p_buf = buf + (size_t)unit_size * read_num; // パリティ・ブロックを記録する領域 + hash = p_buf + (size_t)unit_size * block_lost; + prog_write = source_num >> 5; // 計算で 97%、書き込みで 3% ぐらい + if (prog_write == 0) + prog_write = 1; + prog_base = (__int64)(source_num + prog_write) * block_lost; // ブロックの合計掛け算個数 + 書き込み回数 +#ifdef TIMER + printf("\n read some blocks, and keep all recovering blocks (GPU)\n"); + printf("buffer size = %I64d MB, read_num = %d, round = %d\n", file_off >> 20, read_num, (source_num + read_num - 1) / read_num); +#endif + + // OpenCL の初期化 + cover_max = read_num; // 読み込める分だけにする + len = 0; + i = init_OpenCL(unit_size, &cover_max, &len); + if (i != 0){ + if (i != 3) // GPU が見つからなかった場合はエラー表示しない + printf("init_OpenCL, %d, %d\n", i & 0xFF, i >> 8); + i = free_OpenCL(); + if (i != 0) + printf("free_OpenCL, %d, %d", i & 0xFF, i >> 8); + OpenCL_method = 0; // GPU を使わない設定にする + // GPU を使わずに計算を続行する場合は以下をコメントアウト + err = -3; // CPU だけの方式に切り替える + goto error_end; + } + if (len == 0) // GPUがキャッシュを使わない時だけ、CPU独自にキャッシュの最適化を試みる + len = try_cache_blocking(unit_size); +#ifdef TIMER + printf("cache: limit size = %d, chunk_size = %d, split = %d\n", cpu_cache & 0x7FFF8000, len, (unit_size + len - 1) / len); + printf("prog_base = %I64d, unit_size = %d, method = %d, cover_max = %d\n", prog_base, unit_size, OpenCL_method, cover_max); +#endif + + // マルチ・スレッドの準備をする + th->buf = p_buf; + th->size = unit_size; + th->count = block_lost; + th->off = len; // chunk size + for (j = 0; j < cpu_num1; j++){ // サブ・スレッドごとに + hRun[j] = CreateEvent(NULL, FALSE, FALSE, NULL); // Auto Reset にする + if (hRun[j] == NULL){ + print_win32_err(); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + hEnd[j] = CreateEvent(NULL, TRUE, FALSE, NULL); + if (hEnd[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + // サブ・スレッドを起動する + th->run = hRun[j]; + th->end = hEnd[j]; + th->now = j; // スレッド番号 + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する + if ((j == cpu_num1 - 1) && (OpenCL_method != 0)){ // 最後のスレッドを GPU 管理用にする + hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_decode_gpu, (LPVOID)th, 0, NULL); + } else { + hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_decode_each, (LPVOID)th, 0, NULL); + } + if (hSub[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + CloseHandle(hEnd[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + WaitForSingleObject(hEnd[j], INFINITE); // 設定終了の合図を待つ (リセットしない) + } + // IO が延滞しないように、サブ・スレッド一つの優先度を下げる + SetThreadPriority(hSub[0], THREAD_PRIORITY_BELOW_NORMAL); + + // 何回かに別けてブロックを読み込んで、消失ブロックを少しずつ復元する + print_progress_text(0, "Recovering slice"); + time_last = GetTickCount(); + wcscpy(file_path, base_dir); + parity_now = 0; // 何番目の代替ブロックか + source_off = 0; // 読み込み開始スライス番号 + while (source_off < source_num){ + if (read_num > source_num - source_off) + read_num = source_num - source_off; + th->size = 0xFFFFFFFF; // 1st decode + th->off = source_off - 1; // まだ計算して無い印 + +#ifdef TIMER +read_count = 0; +time_start = GetTickCount(); +#endif + last_file = -1; + for (i = 0; i < read_num; i++){ // スライスを一個ずつ読み込んでメモリー上に配置していく + switch(s_blk[source_off + i].exist){ + case 0: // バッファーにパリティ・ブロックの内容を読み込む + if (file_read_data(rcv_hFile[p_blk[id[parity_now]].file], p_blk[id[parity_now]].off, buf + (size_t)unit_size * i, block_size)){ + printf("file_read_data, recovery slice %d\n", id[parity_now]); + err = 1; + goto error_end; + } + parity_now++; + // パリティ・ブロックのチェックサムを計算する + checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + unit_size - HASH_SIZE), unit_size - HASH_SIZE); +#ifdef TIMER +read_count++; +#endif + break; + case 3: // ソース・ブロックの内容は全て 0 + memset(buf + (size_t)unit_size * i, 0, unit_size); + break; + default: // バッファーにソース・ブロックの内容を読み込む + if (s_blk[source_off + i].file != last_file){ // 別のファイルなら開く + last_file = s_blk[source_off + i].file; + if (hFile){ + CloseHandle(hFile); // 前のファイルを閉じる + hFile = NULL; + } + if (files[last_file].state & 4){ // 上書き中の破損ファイルから読み込む + wcscpy(file_path + base_len, list_buf + files[last_file].name); + } else if (files[last_file].state & 3){ // 作り直した作業ファイルから読み込む + get_temp_name(list_buf + files[last_file].name, file_path + base_len); + } else if (files[last_file].state & 32){ // 名前訂正失敗時には別名ファイルから読み込む + wcscpy(file_path + base_len, list_buf + files[last_file].name2); + } else { // 完全なソース・ファイルから読み込む (追加訂正失敗時も) + wcscpy(file_path + base_len, list_buf + files[last_file].name); + } + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + hFile = NULL; + printf_cp("cannot open file, %s\n", file_path); + err = 1; + goto error_end; + } + } + len = s_blk[source_off + i].size; + file_off = (source_off + i - files[last_file].b_off) * (__int64)block_size; + if (file_read_data(hFile, file_off, buf + (size_t)unit_size * i, len)){ + printf("file_read_data, input slice %d\n", source_off + i); + err = 1; + goto error_end; + } + if (len < block_size) + memset(buf + ((size_t)unit_size * i + len), 0, block_size - len); + // ソース・ブロックのチェックサムを計算する + checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + unit_size - HASH_SIZE), unit_size - HASH_SIZE); +#ifdef TIMER +read_count++; +#endif + } + + if (i + 1 < read_num){ // 最後のブロック以外なら + // サブ・スレッドの動作状況を調べる + j = WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, 0); + if ((j != WAIT_TIMEOUT) && (j != WAIT_FAILED)){ // 計算中でないなら + // 経過表示 + prog_num += block_lost; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + // 計算終了したブロックの次から計算を開始する + th->off += 1; + th->buf = buf + (size_t)unit_size * (th->off - source_off); + th->mat = mat + th->off; + th->now = -1; // 初期値 - 1 + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを再開する + for (j = 0; j < (cpu_num + 1) / 2; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + } + } + } + if (hFile){ // 最後の読み込みファイルを閉じる + CloseHandle(hFile); + hFile = NULL; + } +#ifdef TIMER +time_read += GetTickCount() - time_start; +#endif + + WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, INFINITE); // サブ・スレッドの計算終了の合図を待つ + th->size = 0; // 2nd decode + th->off += 1; // 計算を開始するソース・ブロックの番号 + if (th->off == 0) // エラーや実験時以外は th->off は 0 にならない + memset(p_buf, 0, (size_t)unit_size * block_lost); +#ifdef TIMER + j = (th->off - source_off) * 1000 / read_num; + printf("partial decode = %d (%d.%d%%), read = %d\n", th->off - source_off, j / 10, j % 10, read_count); +#endif + recv_now = -1; // 消失ブロックの本来のソース番号 + last_file = -1; + + // VRAM のサイズに応じて分割する + cover_from = th->off - source_off; + i = (read_num - cover_from + cover_max - 1) / cover_max; // 何回に分けて処理するか + cover_num = (read_num - cover_from + i - 1) / i; // 一度に処理する量を平均化する + while (cover_from < read_num){ + // ソース・ブロックを何個ずつ処理するか + if (cover_from + cover_num > read_num) + cover_num = read_num - cover_from; + //printf("cover_from = %d, cover_num = %d\n", cover_from, cover_num); + + // GPU と CPU がスレッドごとに消失ブロックを計算する + th->buf = buf + (size_t)unit_size * cover_from; + th->mat = mat + (source_off + cover_from); + th->off = source_off + cover_from; // ソース・ブロックの番号にする + th->count = cover_num; + th->now = -1; // 初期値 - 1 + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを再開する + for (j = 0; j < cpu_num1; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + + // サブ・スレッドの計算終了の合図を UPDATE_TIME だけ待つ + while (WaitForMultipleObjects(cpu_num1, hEnd, TRUE, UPDATE_TIME) == WAIT_TIMEOUT){ + // th-now が最高値なので、計算が終わってるのは th-now - cpu_num1 個となる + j = th->now - cpu_num1; + if (j < 0) + j = 0; + // 経過表示(UPDATE_TIME 時間待った場合なので、必ず経過してるはず) + if (print_progress((int)(((prog_num + cover_num * j) * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + if (th->size != 0){ // エラー発生 + i = th->size; + printf("error, gpu-thread, %d, %d\n", i & 0xFF, i >> 8); + err = 1; + goto error_end; + } + + // 経過表示 + prog_num += cover_num * block_lost; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + + cover_from += cover_num; + } + + source_off += read_num; + } + //printf("\nprog_num = %I64d / %I64d\n", prog_num, prog_base); + +#ifdef TIMER +time_start = GetTickCount(); +#endif + // 復元されたブロックを書き込む + work_buf = p_buf; + for (i = 0; i < block_lost; i++){ + for (j = recv_now + 1; j < source_num; j++){ // 何番のソース・ブロックか + if (s_blk[j].exist == 0){ + recv_now = j; + break; + } + } + //printf(" lost block[%d] = source block[%d]\n", i, recv_now); + + // 復元されたソース・ブロックのチェックサムを検証する + checksum16_return(work_buf, hash, unit_size - HASH_SIZE); + if (memcmp(work_buf + unit_size - HASH_SIZE, hash, HASH_SIZE) != 0){ + printf("checksum mismatch, recovered input slice %d\n", recv_now); + err = 1; + goto error_end; + } + // ファイルにソース・ブロックを書き込む + if (s_blk[recv_now].file != last_file){ // 別のファイルなら開く + last_file = s_blk[recv_now].file; + if (hFile){ + CloseHandle(hFile); // 前のファイルを閉じる + hFile = NULL; + } + if (files[last_file].state & 4){ // 破損ファイルを上書きして復元する場合 + // 上書き用のソース・ファイルを開く + hFile = handle_write_file(list_buf + files[last_file].name, file_path, files[last_file].size); + } else { + // 作業ファイルを開く + hFile = handle_temp_file(list_buf + files[last_file].name, file_path); + } + if (hFile == INVALID_HANDLE_VALUE){ + hFile = NULL; + err = 1; + goto error_end; + } + //printf("file %d, open %S\n", last_file, file_path); + } + if (file_write_data(hFile, (recv_now - files[last_file].b_off) * (__int64)block_size, work_buf, s_blk[recv_now].size)){ + printf("file_write_data, input slice %d\n", recv_now); + err = 1; + goto error_end; + } + work_buf += unit_size; + + // 経過表示 + prog_num += prog_write; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + } +#ifdef TIMER +time_write += GetTickCount() - time_start; +#endif + // 最後の書き込みファイルを閉じる + CloseHandle(hFile); + hFile = NULL; + print_progress_done(); + //printf("prog_num = %I64d / %I64d\n", prog_num, prog_base); + +#ifdef TIMER +printf("read %d.%03d sec\n", time_read / 1000, time_read % 1000); +printf("write %d.%03d sec\n", time_write / 1000, time_write % 1000); +#endif + info_OpenCL(buf, MEM_UNIT); // デバイス情報を表示する + +error_end: + InterlockedExchange(&(th->now), INT_MAX / 2); // サブ・スレッドの計算を中断する + for (j = 0; j < cpu_num1; j++){ + if (hSub[j]){ // サブ・スレッドを終了させる + SetEvent(hRun[j]); + WaitForSingleObject(hSub[j], INFINITE); + CloseHandle(hSub[j]); + } + } + if (hFile) + CloseHandle(hFile); + if (buf) + _aligned_free(buf); + i = free_OpenCL(); + if (i != 0) + printf("free_OpenCL, %d, %d", i & 0xFF, i >> 8); + return err; +} + diff --git a/source/par2j/rs_decode.h b/source/par2j/rs_decode.h new file mode 100644 index 0000000..ebf023d --- /dev/null +++ b/source/par2j/rs_decode.h @@ -0,0 +1,57 @@ +#ifndef _RS_DECODE_H_ +#define _RS_DECODE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +int decode_method1( // ソース・ブロックが一個だけの場合 + wchar_t *file_path, + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_r *files, // ソース・ファイルの情報 + source_ctx_r *s_blk, // ソース・ブロックの情報 + parity_ctx_r *p_blk); // パリティ・ブロックの情報 + +int decode_method2( // ソース・データを全て読み込む場合 + wchar_t *file_path, + int block_lost, // 失われたソース・ブロックの数 + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_r *files, // ソース・ファイルの情報 + source_ctx_r *s_blk, // ソース・ブロックの情報 + parity_ctx_r *p_blk, // パリティ・ブロックの情報 + unsigned short *mat); + +int decode_method3( // 復元するブロックを全て保持できる場合 + wchar_t *file_path, + int block_lost, // 失われたソース・ブロックの数 + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_r *files, // ソース・ファイルの情報 + source_ctx_r *s_blk, // ソース・ブロックの情報 + parity_ctx_r *p_blk, // パリティ・ブロックの情報 + unsigned short *mat); + +int decode_method4( // 全てのブロックを断片的に保持する場合 (GPU対応) + wchar_t *file_path, + int block_lost, // 失われたソース・ブロックの数 + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_r *files, // ソース・ファイルの情報 + source_ctx_r *s_blk, // ソース・ブロックの情報 + parity_ctx_r *p_blk, // パリティ・ブロックの情報 + unsigned short *mat); + +int decode_method5( // 復元するブロックだけ保持する場合 (GPU対応) + wchar_t *file_path, + int block_lost, // 失われたソース・ブロックの数 + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_r *files, // ソース・ファイルの情報 + source_ctx_r *s_blk, // ソース・ブロックの情報 + parity_ctx_r *p_blk, // パリティ・ブロックの情報 + unsigned short *mat); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par2j/rs_encode.c b/source/par2j/rs_encode.c new file mode 100644 index 0000000..bed1c0c --- /dev/null +++ b/source/par2j/rs_encode.c @@ -0,0 +1,2292 @@ +// rs_encode.c +// Copyright : 2021-12-17 Yutaka Sawada +// License : GPL + +#ifndef _UNICODE +#define _UNICODE +#endif +#ifndef UNICODE +#define UNICODE +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 // Windows Vista or later +#endif + +#include +#include +#include + +#include + +#include "common2.h" +#include "crc.h" +#include "create.h" +#include "gf16.h" +#include "phmd5.h" +#include "lib_opencl.h" +#include "reedsolomon.h" +#include "rs_encode.h" + + +#ifdef TIMER +static unsigned int time_start, time_read = 0, time_write = 0, time_calc = 0; +static unsigned int read_count, skip_count; +#endif + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// マルチスレッドCPU用のサブ・スレッド + +typedef struct { // RS threading control struct + unsigned short *mat; // 行列 + unsigned char * volatile buf; + volatile unsigned int size; // バイト数 + volatile int count; + volatile int off; + volatile int now; + HANDLE run; + HANDLE end; +} RS_TH; + +// chunk ごとに計算するためのスレッド +static DWORD WINAPI thread_encode2(LPVOID lpParameter) +{ + unsigned char *s_buf, *p_buf, *work_buf; + unsigned short *constant, factor2; + volatile unsigned short *factor1; + int i, j, src_start, src_num, max_num, chunk_num; + int part_start, part_num, cover_num; + unsigned int unit_size, len, off, chunk_size; + HANDLE hRun, hEnd; + RS_TH *th; +#ifdef TIMER +unsigned int loop_count2a = 0, loop_count2b = 0; +unsigned int time_start2, time_encode2a = 0, time_encode2b = 0; +#endif + + th = (RS_TH *)lpParameter; + constant = th->mat; + p_buf = th->buf; + unit_size = th->size; + chunk_size = th->off; + part_num = th->count; + hRun = th->run; + hEnd = th->end; + //_mm_sfence(); + SetEvent(hEnd); // 設定完了を通知する + + factor1 = constant + source_num; + chunk_num = (unit_size + chunk_size - 1) / chunk_size; + + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + while (th->now < INT_MAX / 2){ +#ifdef TIMER +time_start2 = GetTickCount(); +#endif + s_buf = th->buf; + src_start = th->off; // ソース・ブロック番号 + len = chunk_size; + + if (th->size == 0){ // ソース・ブロック読み込み中 + // パリティ・ブロックごとに掛け算して追加していく + max_num = chunk_num * part_num; + while ((j = InterlockedIncrement(&(th->now))) < max_num){ // j = ++th_now + off = j / part_num; // chunk の番号 + j = j % part_num; // parity の番号 + off *= chunk_size; + if (off + len > unit_size) + len = unit_size - off; // 最後の chunk だけサイズが異なるかも + if (src_start == 0) // 最初のブロックを計算する際に + memset(p_buf + ((size_t)unit_size * j + off), 0, len); // ブロックを 0で埋める + galois_align_multiply(s_buf + off, p_buf + ((size_t)unit_size * j + off), len, factor1[j]); +#ifdef TIMER +loop_count2a++; +#endif + } +#ifdef TIMER +time_encode2a += GetTickCount() - time_start2; +#endif + } else { // パリティ・ブロックを部分的に保持する場合 + // スレッドごとに作成するパリティ・ブロックの chunk を変える + src_num = source_num - src_start; + cover_num = th->size; + part_start = th->count; + max_num = chunk_num * cover_num; + while ((j = InterlockedIncrement(&(th->now))) < max_num){ // j = ++th_now + off = j / cover_num; // chunk の番号 + j = j % cover_num; // parity の番号 + off *= chunk_size; // chunk の位置 + if (off + len > unit_size) + len = unit_size - off; // 最後の chunk だけサイズが異なるかも + work_buf = p_buf + (size_t)unit_size * j + off; + if (part_start != 0) + memset(work_buf, 0, len); // 最初の part_num 以降は 2nd encode だけなので 0で埋める + + // ソース・ブロックごとにパリティを追加していく + for (i = 0; i < src_num; i++){ + factor2 = galois_power(constant[src_start + i], first_num + part_start + j); // factor は定数行列の乗数になる + galois_align_multiply(s_buf + ((size_t)unit_size * i + off), work_buf, len, factor2); + } +#ifdef TIMER +loop_count2b += src_num; +#endif + } +#ifdef TIMER +time_encode2b += GetTickCount() - time_start2; +#endif + } + //_mm_sfence(); // メモリーへの書き込みを完了する + SetEvent(hEnd); // 計算終了を通知する + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + } +#ifdef TIMER +loop_count2a /= chunk_num; // chunk数で割ってブロック数にする +loop_count2b /= chunk_num; +printf("sub-thread : total loop = %d\n", loop_count2a + loop_count2b); +if (time_encode2a > 0){ + i = (int)((__int64)loop_count2a * unit_size * 125 / ((__int64)time_encode2a * 131072)); +} else { + i = 0; +} +if (loop_count2a > 0) + printf(" 1st encode %d.%03d sec, %d loop, %d MB/s\n", time_encode2a / 1000, time_encode2a % 1000, loop_count2a, i); +if (time_encode2b > 0){ + i = (int)((__int64)loop_count2b * unit_size * 125 / ((__int64)time_encode2b * 131072)); +} else { + i = 0; +} +printf(" 2nd encode %d.%03d sec, %d loop, %d MB/s\n", time_encode2b / 1000, time_encode2b % 1000, loop_count2b, i); +#endif + + // 終了処理 + CloseHandle(hRun); + CloseHandle(hEnd); + return 0; +} + +static DWORD WINAPI thread_encode3(LPVOID lpParameter) +{ + unsigned char *s_buf, *p_buf, *work_buf; + unsigned short *constant, factor2; + volatile unsigned short *factor1; + int i, j, src_start, src_num, max_num, chunk_num; + unsigned int unit_size, len, off, chunk_size; + HANDLE hRun, hEnd; + RS_TH *th; +#ifdef TIMER +unsigned int loop_count2a = 0, loop_count2b = 0; +unsigned int time_start2, time_encode2a = 0, time_encode2b = 0; +#endif + + th = (RS_TH *)lpParameter; + constant = th->mat; + p_buf = th->buf; + unit_size = th->size; + chunk_size = th->off; + hRun = th->run; + hEnd = th->end; + //_mm_sfence(); + SetEvent(hEnd); // 設定完了を通知する + + factor1 = constant + source_num; + chunk_num = (unit_size + chunk_size - 1) / chunk_size; + max_num = chunk_num * parity_num; + + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + while (th->now < INT_MAX / 2){ +#ifdef TIMER +time_start2 = GetTickCount(); +#endif + s_buf = th->buf; + src_start = th->off; // ソース・ブロック番号 + len = chunk_size; + + if (th->size == 0){ // ソース・ブロック読み込み中 + // パリティ・ブロックごとに掛け算して追加していく + while ((j = InterlockedIncrement(&(th->now))) < max_num){ // j = ++th_now + off = j / parity_num; // chunk の番号 + j = j % parity_num; // parity の番号 + off *= chunk_size; + if (off + len > unit_size) + len = unit_size - off; // 最後の chunk だけサイズが異なるかも + if (src_start == 0) // 最初のブロックを計算する際に + memset(p_buf + ((size_t)unit_size * j + off), 0, len); // ブロックを 0で埋める + galois_align_multiply(s_buf + off, p_buf + ((size_t)unit_size * j + off), len, factor1[j]); +#ifdef TIMER +loop_count2a++; +#endif + } +#ifdef TIMER +time_encode2a += GetTickCount() - time_start2; +#endif + } else { // 全てのパリティ・ブロックを保持する場合 + // スレッドごとに作成するパリティ・ブロックの chunk を変える + src_num = th->size; + while ((j = InterlockedIncrement(&(th->now))) < max_num){ // j = ++th_now + off = j / parity_num; // chunk の番号 + j = j % parity_num; // parity の番号 + off *= chunk_size; // chunk の位置 + if (off + len > unit_size) + len = unit_size - off; // 最後の chunk だけサイズが異なるかも + work_buf = p_buf + (size_t)unit_size * j + off; + + // ソース・ブロックごとにパリティを追加していく + for (i = 0; i < src_num; i++){ + factor2 = galois_power(constant[src_start + i], first_num + j); // factor は定数行列の乗数になる + galois_align_multiply(s_buf + ((size_t)unit_size * i + off), work_buf, len, factor2); + } +#ifdef TIMER +loop_count2b += src_num; +#endif + } +#ifdef TIMER +time_encode2b += GetTickCount() - time_start2; +#endif + } + //_mm_sfence(); // メモリーへの書き込みを完了する + SetEvent(hEnd); // 計算終了を通知する + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + } +#ifdef TIMER +loop_count2a /= chunk_num; // chunk数で割ってブロック数にする +loop_count2b /= chunk_num; +printf("sub-thread : total loop = %d\n", loop_count2a + loop_count2b); +if (time_encode2a > 0){ + i = (int)((__int64)loop_count2a * unit_size * 125 / ((__int64)time_encode2a * 131072)); +} else { + i = 0; +} +if (loop_count2a > 0) + printf(" 1st encode %d.%03d sec, %d loop, %d MB/s\n", time_encode2a / 1000, time_encode2a % 1000, loop_count2a, i); +if (time_encode2b > 0){ + i = (int)((__int64)loop_count2b * unit_size * 125 / ((__int64)time_encode2b * 131072)); +} else { + i = 0; +} +printf(" 2nd encode %d.%03d sec, %d loop, %d MB/s\n", time_encode2b / 1000, time_encode2b % 1000, loop_count2b, i); +#endif + + // 終了処理 + CloseHandle(hRun); + CloseHandle(hEnd); + return 0; +} + +// ブロックごとに計算するためのスレッド +static DWORD WINAPI thread_encode_each(LPVOID lpParameter) +{ + unsigned char *s_buf, *p_buf, *work_buf; + unsigned short *constant, *factor2; + volatile unsigned short *factor1; + int i, j, th_id, src_start, src_num, max_num; + unsigned int unit_size, len, off, chunk_size; + HANDLE hRun, hEnd; + RS_TH *th; +#ifdef TIMER +unsigned int loop_count2a = 0, loop_count2b = 0; +unsigned int time_start2, time_encode2a = 0, time_encode2b = 0; +#endif + + th = (RS_TH *)lpParameter; + constant = th->mat; + p_buf = th->buf; + unit_size = th->size; + th_id = th->now; // スレッド番号 + chunk_size = th->off; + factor2 = (unsigned short *)(p_buf + ((size_t)unit_size * parity_num + HASH_SIZE)); + factor2 += th->count * th_id; // スレッドごとに保存場所を変える + hRun = th->run; + hEnd = th->end; + //_mm_sfence(); + SetEvent(hEnd); // 設定完了を通知する + + factor1 = constant + source_num; + max_num = ((unit_size + chunk_size - 1) / chunk_size) * parity_num; + + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + while (th->now < INT_MAX / 2){ +#ifdef TIMER +time_start2 = GetTickCount(); +#endif + s_buf = th->buf; + src_start = th->off; // ソース・ブロック番号 + + if (th->size == 0xFFFFFFFF){ // ソース・ブロック読み込み中 + len = chunk_size; + // パリティ・ブロックごとに掛け算して追加していく + while ((j = InterlockedIncrement(&(th->now))) < max_num){ // j = ++th_now + off = j / parity_num; // chunk の番号 + j = j % parity_num; // parity の番号 + off *= chunk_size; + if (off + len > unit_size) + len = unit_size - off; // 最後の chunk だけサイズが異なるかも + if (src_start == 0) // 最初のブロックを計算する際に + memset(p_buf + ((size_t)unit_size * j + off), 0, len); // ブロックを 0で埋める + galois_align_multiply(s_buf + off, p_buf + ((size_t)unit_size * j + off), len, factor1[j]); +#ifdef TIMER +loop_count2a++; +#endif + } +#ifdef TIMER +time_encode2a += GetTickCount() - time_start2; +#endif + } else { + // スレッドごとに作成するパリティ・ブロックを変える + src_num = th->count; + while ((j = InterlockedIncrement(&(th->now))) < parity_num){ // j = ++th_now + work_buf = p_buf + (size_t)unit_size * j; + + // factor は定数行列の乗数になる + for (i = 0; i < src_num; i++) + factor2[i] = galois_power(constant[src_start + i], first_num + j); + + // chunk に分割して計算する + len = chunk_size; + off = 0; + while (off < unit_size){ + // ソース・ブロックごとにパリティを追加していく + for (i = 0; i < src_num; i++) + galois_align_multiply(s_buf + ((size_t)unit_size * i + off), work_buf, len, factor2[i]); + + work_buf += len; + off += len; + if (off + len > unit_size) + len = unit_size - off; + } +#ifdef TIMER +loop_count2b += src_num; +#endif + } +#ifdef TIMER +time_encode2b += GetTickCount() - time_start2; +#endif + } + //_mm_sfence(); // メモリーへの書き込みを完了する + SetEvent(hEnd); // 計算終了を通知する + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + } +#ifdef TIMER +loop_count2a /= (unit_size + chunk_size - 1) / chunk_size; // chunk数で割ってブロック数にする +printf("sub-thread[%d] : total loop = %d\n", th_id, loop_count2a + loop_count2b); +if (time_encode2a > 0){ + i = (int)((__int64)loop_count2a * unit_size * 125 / ((__int64)time_encode2a * 131072)); +} else { + i = 0; +} +if (loop_count2a > 0) + printf(" 1st encode %d.%03d sec, %d loop, %d MB/s\n", time_encode2a / 1000, time_encode2a % 1000, loop_count2a, i); +if (time_encode2b > 0){ + i = (int)((__int64)loop_count2b * unit_size * 125 / ((__int64)time_encode2b * 131072)); +} else { + i = 0; +} +printf(" 2nd encode %d.%03d sec, %d loop, %d MB/s\n", time_encode2b / 1000, time_encode2b % 1000, loop_count2b, i); +#endif + + // 終了処理 + CloseHandle(hRun); + CloseHandle(hEnd); + return 0; +} + +// GPU 対応のサブ・スレッド (スレッド番号は最後になる) +static DWORD WINAPI thread_encode_gpu(LPVOID lpParameter) +{ + unsigned char *s_buf, *p_buf; + unsigned short *constant, *factor2; + int i, j, th_id, src_start, src_num; + unsigned int unit_size; + HANDLE hRun, hEnd; + RS_TH *th; +#ifdef TIMER +unsigned int time_start2, time_encode2 = 0, loop_count2 = 0; +#endif + + th = (RS_TH *)lpParameter; + constant = th->mat; + p_buf = th->buf; + unit_size = th->size; + th_id = th->now; // スレッド番号 + factor2 = (unsigned short *)(p_buf + ((size_t)unit_size * parity_num + HASH_SIZE)); + factor2 += th->count * th_id; // スレッドごとに保存場所を変える + hRun = th->run; + hEnd = th->end; + //_mm_sfence(); + SetEvent(hEnd); // 設定完了を通知する + + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + while (th->now < INT_MAX / 2){ +#ifdef TIMER +time_start2 = GetTickCount(); +#endif + // GPUはソース・ブロック読み込み中に呼ばれない + s_buf = th->buf; + src_start = th->off; // ソース・ブロック番号 + src_num = th->count; + + // 最初にソース・ブロックをVRAMへ転送する + i = gpu_copy_blocks(s_buf, unit_size, src_num); + if (i != 0){ + th->size = i; + InterlockedExchange(&(th->now), INT_MAX / 2); // サブ・スレッドの計算を中断する + } + + // スレッドごとに作成するパリティ・ブロックを変える + while ((j = InterlockedIncrement(&(th->now))) < parity_num){ // j = ++th_now + // factor は定数行列の乗数になる + for (i = 0; i < src_num; i++) + factor2[i] = galois_power(constant[src_start + i], first_num + j); + + i = gpu_multiply_blocks(src_num, factor2, p_buf + (size_t)unit_size * j, unit_size); + if (i != 0){ + th->size = i; + break; + } +#ifdef TIMER +loop_count2 += src_num; +#endif + } +#ifdef TIMER +time_encode2 += GetTickCount() - time_start2; +#endif + // 最後にVRAMを解放する + th->size = gpu_finish(); + + //_mm_sfence(); // メモリーへの書き込みを完了する + SetEvent(hEnd); // 計算終了を通知する + WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ + } +#ifdef TIMER +printf("gpu-thread : total loop = %d\n", loop_count2); +if (time_encode2 > 0){ + i = (int)((__int64)loop_count2 * unit_size * 125 / ((__int64)time_encode2 * 131072)); +} else { + i = 0; +} +printf(" 2nd encode %d.%03d sec, %d loop, %d MB/s\n", time_encode2 / 1000, time_encode2 % 1000, loop_count2, i); +#endif + + // 終了処理 + CloseHandle(hRun); + CloseHandle(hEnd); + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int encode_method1( // ソース・ブロックが一個だけの場合 + wchar_t *file_path, + unsigned char *header_buf, // Recovery Slice packet のパケット・ヘッダー + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_c *files, // ソース・ファイルの情報 + source_ctx_c *s_blk, // ソース・ブロックの情報 + parity_ctx_c *p_blk) // パリティ・ブロックの情報 +{ + unsigned char *buf = NULL, *work_buf, *hash; + int err = 0, i, j; + unsigned int io_size, unit_size, len, block_off; + unsigned int time_last, prog_num = 0, prog_base; + HANDLE hFile = NULL; + PHMD5 md_ctx, *md_ptr = NULL; + + // 作業バッファーを確保する + io_size = get_io_size(2, NULL, 1, sse_unit); + //io_size = (((io_size + 2) / 3 + HASH_SIZE + (sse_unit - 1)) & ~(sse_unit - 1)) - HASH_SIZE; // 実験用 + unit_size = io_size + HASH_SIZE; // チェックサムの分だけ増やす + len = 2 * unit_size + HASH_SIZE; + buf = _aligned_malloc(len, sse_unit); + if (buf == NULL){ + printf("malloc, %d\n", len); + err = 1; + goto error_end; + } + work_buf = buf + unit_size; + hash = work_buf + unit_size; + prog_base = (block_size + io_size - 1) / io_size; + prog_base *= parity_num; // 全体の断片の個数 +#ifdef TIMER + printf("\n read one source block, and keep one parity block\n"); + printf("buffer size = %d MB, io_size = %d, split = %d\n", len >> 20, io_size, (block_size + io_size - 1) / io_size); + j = try_cache_blocking(unit_size); + printf("cache: limit size = %d, chunk_size = %d, split = %d\n", cpu_cache & 0x7FFF8000, j, (unit_size + j - 1) / j); +#endif + + if (io_size < block_size){ // スライスが分割される場合だけ、途中までのハッシュ値を保持する + len = sizeof(PHMD5) * parity_num; + md_ptr = malloc(len); + if (md_ptr == NULL){ + printf("malloc, %d\n", len); + err = 1; + goto error_end; + } + for (i = 0; i < parity_num; i++){ + Phmd5Begin(&(md_ptr[i])); + j = first_num + i; // 最初の番号の分だけ足す + memcpy(header_buf + 64, &j, 4); // Recovery Slice の番号を書き込む + Phmd5Process(&(md_ptr[i]), header_buf + 32, 36); + } + } + + // ソース・ファイルを開く + wcscpy(file_path, base_dir); + wcscpy(file_path + base_len, list_buf + files[s_blk[0].file].name); + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + hFile = NULL; + printf_cp("cannot open file, %s\n", list_buf + files[s_blk[0].file].name); + err = 1; + goto error_end; + } + + // バッファー・サイズごとにパリティ・ブロックを作成する + time_last = GetTickCount(); + s_blk[0].crc = 0xFFFFFFFF; + block_off = 0; + while (block_off < block_size){ +#ifdef TIMER +time_start = GetTickCount(); +#endif + // ソース・ブロックを読み込む + len = s_blk[0].size - block_off; + if (len > io_size) + len = io_size; + if (file_read_data(hFile, (__int64)block_off, buf, len)){ + printf("file_read_data, input slice %d\n", i); + err = 1; + goto error_end; + } + if (len < io_size) + memset(buf + len, 0, io_size - len); + // ソース・ブロックのチェックサムを計算する + s_blk[0].crc = crc_update(s_blk[0].crc, buf, len); // without pad + checksum16_altmap(buf, buf + io_size, io_size); +#ifdef TIMER +time_read += GetTickCount() - time_start; +#endif + + // リカバリ・ファイルに書き込むサイズ + if (block_size - block_off < io_size){ + len = block_size - block_off; + } else { + len = io_size; + } + + // パリティ・ブロックごとに + for (i = 0; i < parity_num; i++){ +#ifdef TIMER +time_start = GetTickCount(); +#endif + memset(work_buf, 0, unit_size); + // factor は 2の乗数になる + galois_align_multiply(buf, work_buf, unit_size, galois_power(2, first_num + i)); +#ifdef TIMER +time_calc += GetTickCount() - time_start; +#endif + + // 経過表示 + prog_num++; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((prog_num * 1000) / prog_base)){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + +#ifdef TIMER +time_start = GetTickCount(); +#endif + // パリティ・ブロックのチェックサムを検証する + checksum16_return(work_buf, hash, io_size); + if (memcmp(work_buf + io_size, hash, HASH_SIZE) != 0){ + printf("checksum mismatch, recovery slice %d\n", i); + err = 1; + goto error_end; + } + // ハッシュ値を計算して、リカバリ・ファイルに書き込む + if (io_size >= block_size){ // 1回で書き込みが終わるなら + Phmd5Begin(&md_ctx); + j = first_num + i; // 最初の番号の分だけ足す + memcpy(header_buf + 64, &j, 4); // Recovery Slice の番号を書き込む + Phmd5Process(&md_ctx, header_buf + 32, 36); + Phmd5Process(&md_ctx, work_buf, len); + Phmd5End(&md_ctx); + memcpy(header_buf + 16, md_ctx.hash, 16); + // ヘッダーを書き込む + if (file_write_data(rcv_hFile[p_blk[i].file], p_blk[i].off + block_off - 68, header_buf, 68)){ + printf("file_write_data, recovery slice %d\n", i); + err = 1; + goto error_end; + } + } else { + Phmd5Process(&(md_ptr[i]), work_buf, len); + } + if (file_write_data(rcv_hFile[p_blk[i].file], p_blk[i].off + block_off, work_buf, len)){ + printf("file_write_data, recovery slice %d\n", i); + err = 1; + goto error_end; + } +#ifdef TIMER +time_write += GetTickCount() - time_start; +#endif + } + + block_off += io_size; + } + print_progress_done(); // 改行して行の先頭に戻しておく + + // ファイルごとにブロックの CRC-32 を検証する + if (s_blk[0].size < block_size){ // 残りを 0 でパディングする + len = block_size - s_blk[0].size; + memset(buf, 0, len); + s_blk[0].crc = crc_update(s_blk[0].crc, buf, len); + } + s_blk[0].crc ^= 0xFFFFFFFF; + if (((unsigned int *)(files[s_blk[0].file].hash))[0] != s_blk[0].crc){ + printf("checksum mismatch, input file %d\n", s_blk[0].file); + err = 1; + goto error_end; + } + + if (io_size < block_size){ // 1回で書き込みが終わらなかったなら + if (GetTickCount() - time_last >= UPDATE_TIME){ // キャンセルを受け付ける + if (cancel_progress()){ + err = 2; + goto error_end; + } + } + +#ifdef TIMER +time_start = GetTickCount(); +#endif + // 最後に Recovery Slice packet のヘッダーを書き込む + for (i = 0; i < parity_num; i++){ + Phmd5End(&(md_ptr[i])); + memcpy(header_buf + 16, md_ptr[i].hash, 16); + j = first_num + i; // 最初のパリティ・ブロック番号の分だけ足す + memcpy(header_buf + 64, &j, 4); // Recovery Slice の番号を書き込む + // リカバリ・ファイルに書き込む + if (file_write_data(rcv_hFile[p_blk[i].file], p_blk[i].off - 68, header_buf, 68)){ // ヘッダーのサイズ分だけずらす + printf("file_write_data, packet header\n"); + err = 1; + goto error_end; + } + } +#ifdef TIMER +time_write += GetTickCount() - time_start; +#endif + } + +#ifdef TIMER +printf("read %d.%03d sec\n", time_read / 1000, time_read % 1000); +printf("write %d.%03d sec\n", time_write / 1000, time_write % 1000); +printf("encode %d.%03d sec\n", time_calc / 1000, time_calc % 1000); +#endif + +error_end: + if (md_ptr) + free(md_ptr); + if (hFile) + CloseHandle(hFile); + if (buf) + _aligned_free(buf); + return err; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// chunk ごとに計算するバージョン Cache Blocking for CPU's L3 cache + +int encode_method2( // ソース・データを全て読み込む場合 + wchar_t *file_path, + unsigned char *header_buf, // Recovery Slice packet のパケット・ヘッダー + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_c *files, // ソース・ファイルの情報 + source_ctx_c *s_blk, // ソース・ブロックの情報 + parity_ctx_c *p_blk, // パリティ・ブロックの情報 + unsigned short *constant) // 複数ブロック分の領域を確保しておく? +{ + unsigned char *buf = NULL, *p_buf, *work_buf, *hash; + unsigned short *factor1; + int err = 0, i, j, last_file, part_start, part_num; + int src_num, chunk_num, cover_num; + unsigned int io_size, unit_size, len, block_off; + unsigned int time_last, prog_write; + __int64 file_off, prog_num = 0, prog_base; + HANDLE hFile = NULL; + HANDLE hSub[MAX_CPU], hRun[MAX_CPU], hEnd[MAX_CPU]; + RS_TH th[1]; + PHMD5 md_ctx, *md_ptr = NULL; + + memset(hSub, 0, sizeof(HANDLE) * MAX_CPU); + factor1 = constant + source_num; + + // 作業バッファーを確保する + part_num = source_num >> PART_MAX_RATE; // ソース・ブロック数に対する割合で最大量を決める + //part_num = (parity_num + 1) / 2; // 確保量の実験用 + //part_num = (parity_num + 2) / 3; // 確保量の実験用 + if (part_num < parity_num){ // 分割して計算するなら + i = (parity_num + part_num - 1) / part_num; // 分割回数 + part_num = (parity_num + i - 1) / i; + part_num = ((part_num + cpu_num - 1) / cpu_num) * cpu_num; // cpu_num の倍数にする(切り上げ) + } + if (part_num > parity_num) + part_num = parity_num; + io_size = get_io_size(source_num, &part_num, 1, sse_unit); + //io_size = (((io_size + 1) / 2 + HASH_SIZE + (sse_unit - 1)) & ~(sse_unit - 1)) - HASH_SIZE; // 2分割の実験用 + //io_size = (((io_size + 2) / 3 + HASH_SIZE + (sse_unit - 1)) & ~(sse_unit - 1)) - HASH_SIZE; // 3分割の実験用 + unit_size = io_size + HASH_SIZE; // チェックサムの分だけ増やす + file_off = (source_num + part_num) * (size_t)unit_size + HASH_SIZE; + buf = _aligned_malloc((size_t)file_off, sse_unit); + if (buf == NULL){ + printf("malloc, %I64d\n", file_off); + err = 1; + goto error_end; + } + p_buf = buf + (size_t)unit_size * source_num; // パリティ・ブロックを部分的に記録する領域 + hash = p_buf + (size_t)unit_size * part_num; + prog_base = (block_size + io_size - 1) / io_size; + prog_write = source_num >> 5; // 計算で 97%、書き込みで 3% ぐらい + if (prog_write == 0) + prog_write = 1; + prog_base *= (__int64)(source_num + prog_write) * parity_num; // 全体の断片の個数 + len = try_cache_blocking(unit_size); + //len = ((len + 2) / 3 + (sse_unit - 1)) & ~(sse_unit - 1); // 1/3の実験用 + chunk_num = (unit_size + len - 1) / len; +#ifdef TIMER + printf("\n read all source blocks, and keep some parity blocks\n"); + printf("buffer size = %I64d MB, io_size = %d, split = %d\n", file_off >> 20, io_size, (block_size + io_size - 1) / io_size); + printf("cache: limit size = %d, chunk_size = %d, split = %d\n", cpu_cache & 0x7FFF8000, len, chunk_num); + printf("prog_base = %I64d, unit_size = %d, part_num = %d\n", prog_base, unit_size, part_num); +#endif + + if (io_size < block_size){ // スライスが分割される場合だけ、途中までのハッシュ値を保持する + block_off = sizeof(PHMD5) * parity_num; + md_ptr = malloc(block_off); + if (md_ptr == NULL){ + printf("malloc, %d\n", block_off); + err = 1; + goto error_end; + } + for (i = 0; i < parity_num; i++){ + Phmd5Begin(&(md_ptr[i])); + j = first_num + i; // 最初の番号の分だけ足す + memcpy(header_buf + 64, &j, 4); // Recovery Slice の番号を書き込む + Phmd5Process(&(md_ptr[i]), header_buf + 32, 36); + } + } + + // マルチ・スレッドの準備をする + th->mat = constant; + th->buf = p_buf; + th->size = unit_size; + th->count = part_num; + th->off = len; // キャッシュの最適化を試みる + for (j = 0; j < cpu_num; j++){ // サブ・スレッドごとに + hRun[j] = CreateEvent(NULL, FALSE, FALSE, NULL); // Auto Reset にする + if (hRun[j] == NULL){ + print_win32_err(); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + hEnd[j] = CreateEvent(NULL, TRUE, FALSE, NULL); + if (hEnd[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + // サブ・スレッドを起動する + th->run = hRun[j]; + th->end = hEnd[j]; + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する + hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_encode2, (LPVOID)th, 0, NULL); + if (hSub[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + CloseHandle(hEnd[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + WaitForSingleObject(hEnd[j], INFINITE); // 設定終了の合図を待つ (リセットしない) + } + // IO が延滞しないように、サブ・スレッド一つの優先度を下げる + SetThreadPriority(hSub[0], THREAD_PRIORITY_BELOW_NORMAL); + + // ソース・ブロック断片を読み込んで、パリティ・ブロック断片を作成する + time_last = GetTickCount(); + wcscpy(file_path, base_dir); + block_off = 0; + while (block_off < block_size){ + th->size = 0; // 1st encode + th->off = -1; // まだ計算して無い印 + + // ソース・ブロックを読み込む +#ifdef TIMER +read_count = 0; +skip_count = 0; +time_start = GetTickCount(); +#endif + last_file = -1; + for (i = 0; i < source_num; i++){ + if (s_blk[i].file != last_file){ // 別のファイルなら開く + last_file = s_blk[i].file; + if (hFile){ + CloseHandle(hFile); // 前のファイルを閉じる + hFile = NULL; + } + wcscpy(file_path + base_len, list_buf + files[last_file].name); + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + hFile = NULL; + printf_cp("cannot open file, %s\n", list_buf + files[last_file].name); + err = 1; + goto error_end; + } + file_off = block_off; + } else { // 同じファイルならブロック・サイズ分ずらす + file_off += block_size; + } + if (s_blk[i].size > block_off){ // バッファーにソース・ファイルの内容を読み込む + len = s_blk[i].size - block_off; + if (len > io_size) + len = io_size; + if (file_read_data(hFile, file_off, buf + (size_t)unit_size * i, len)){ + printf("file_read_data, input slice %d\n", i); + err = 1; + goto error_end; + } + if (len < io_size) + memset(buf + ((size_t)unit_size * i + len), 0, io_size - len); + // ソース・ブロックのチェックサムを計算する + if (block_off == 0) + s_blk[i].crc = 0xFFFFFFFF; + s_blk[i].crc = crc_update(s_blk[i].crc, buf + (size_t)unit_size * i, len); // without pad + checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + io_size), io_size); +#ifdef TIMER +read_count++; +#endif + + if (i + 1 < source_num){ // 最後のブロック以外なら + // サブ・スレッドの動作状況を調べる + j = WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, 0); + if ((j != WAIT_TIMEOUT) && (j != WAIT_FAILED)){ // 計算中でないなら + // 経過表示 + prog_num += part_num; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + // 計算終了したブロックの次から計算を開始する + th->off += 1; + if (th->off > 0){ // バッファーに読み込んだ時だけ計算する + while (s_blk[th->off].size <= block_off){ + prog_num += part_num; + th->off += 1; +#ifdef TIMER +skip_count++; +#endif + } + } + th->buf = buf + (size_t)unit_size * th->off; + for (j = 0; j < part_num; j++) + factor1[j] = galois_power(constant[th->off], first_num + j); // factor は定数行列の乗数になる + th->now = -1; // 初期値 - 1 + //_mm_sfence(); + for (j = 0; j < (cpu_num + 1) / 2; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + } + } + } else { + memset(buf + (size_t)unit_size * i, 0, unit_size); + } + } + // 最後のソース・ファイルを閉じる + CloseHandle(hFile); + hFile = NULL; +#ifdef TIMER +time_read += GetTickCount() - time_start; +#endif + + WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, INFINITE); // サブ・スレッドの計算終了の合図を待つ + th->off += 1; // 計算を開始するソース・ブロックの番号 + if (th->off > 0){ + while (s_blk[th->off].size <= block_off){ // 計算不要なソース・ブロックはとばす + prog_num += part_num; + th->off += 1; +#ifdef TIMER +skip_count++; +#endif + } + } else { // エラーや実験時以外は th->off は 0 にならない + memset(p_buf, 0, (size_t)unit_size * part_num); + } +#ifdef TIMER + j = (th->off * 1000) / source_num; + printf("partial encode = %d / %d (%d.%d%%), read = %d, skip = %d\n", th->off, source_num, j / 10, j % 10, read_count, skip_count); + // ここまでのパリティ・ブロックのチェックサムを検証する +/* if (th->off > 0){ + for (j = 0; j < part_num; j++){ + checksum16_return(p_buf + (size_t)unit_size * j, hash, io_size); + if (memcmp(p_buf + ((size_t)unit_size * j + io_size), hash, HASH_SIZE) != 0){ + printf("checksum mismatch, recovery slice %d after 1st encode\n", j); + err = 1; + goto error_end; + } + galois_altmap_change(p_buf + (size_t)unit_size * j, unit_size); + } + }*/ +#endif + + // リカバリ・ファイルに書き込むサイズ + if (block_size - block_off < io_size){ + len = block_size - block_off; + } else { + len = io_size; + } + + // cover_num ごとに処理する + part_start = 0; + cover_num = part_num; // part_num は cpu_num の倍数にすること + src_num = source_num - th->off; // 一度に処理する量 (src_num > 0) + th->buf = buf + (size_t)unit_size * (th->off); + while (part_start < parity_num){ + if (part_start == part_num){ // part_num 分の計算が終わったら + th->off = 0; // 最初の計算以降は全てのソース・ブロックを対象にする + src_num = source_num; // source_num - th->off + th->buf = buf; // buf + (size_t)unit_size * (th->off); + } + if (part_start + cover_num > parity_num) + cover_num = parity_num - part_start; + //printf("part_start = %d, src_num = %d / %d, cover_num = %d\n", part_start, src_num, source_num, cover_num); + + // スレッドごとにパリティ・ブロックを計算する + th->size = cover_num; + th->count = part_start; + th->now = -1; // 初期値 - 1 + //_mm_sfence(); + for (j = 0; j < cpu_num; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + + // サブ・スレッドの計算終了の合図を UPDATE_TIME だけ待ちながら、経過表示する + while (WaitForMultipleObjects(cpu_num, hEnd, TRUE, UPDATE_TIME) == WAIT_TIMEOUT){ + // th-now が最高値なので、計算が終わってるのは th-now - cpu_num 個となる + j = th->now - cpu_num; + if (j < 0) + j = 0; + j /= chunk_num; // chunk数で割ってブロック数にする + // 経過表示(UPDATE_TIME 時間待った場合なので、必ず経過してるはず) + if (print_progress((int)(((prog_num + src_num * j) * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + prog_num += src_num * cover_num; + +#ifdef TIMER +time_start = GetTickCount(); +#endif + // パリティ・ブロックを書き込む + work_buf = p_buf; + for (i = part_start; i < part_start + cover_num; i++){ + // パリティ・ブロックのチェックサムを検証する + checksum16_return(work_buf, hash, io_size); + if (memcmp(work_buf + io_size, hash, HASH_SIZE) != 0){ + printf("checksum mismatch, recovery slice %d\n", i); + err = 1; + goto error_end; + } + // ハッシュ値を計算して、リカバリ・ファイルに書き込む + if (io_size >= block_size){ // 1回で書き込みが終わるなら + Phmd5Begin(&md_ctx); + j = first_num + i; // 最初の番号の分だけ足す + memcpy(header_buf + 64, &j, 4); // Recovery Slice の番号を書き込む + Phmd5Process(&md_ctx, header_buf + 32, 36); + Phmd5Process(&md_ctx, work_buf, len); + Phmd5End(&md_ctx); + memcpy(header_buf + 16, md_ctx.hash, 16); + // ヘッダーを書き込む + if (file_write_data(rcv_hFile[p_blk[i].file], p_blk[i].off + block_off - 68, header_buf, 68)){ + printf("file_write_data, recovery slice %d\n", i); + err = 1; + goto error_end; + } + } else { + Phmd5Process(&(md_ptr[i]), work_buf, len); + } + //printf("%d, buf = %p, size = %u, off = %I64d\n", i, work_buf, len, p_blk[i].off + block_off); + if (file_write_data(rcv_hFile[p_blk[i].file], p_blk[i].off + block_off, work_buf, len)){ + printf("file_write_data, recovery slice %d\n", i); + err = 1; + goto error_end; + } + work_buf += unit_size; + + // 経過表示 + prog_num += prog_write; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + } +#ifdef TIMER +time_write += GetTickCount() - time_start; +#endif + + part_start += part_num; // 次のパリティ位置にする + } + + block_off += io_size; + } + print_progress_done(); // 改行して行の先頭に戻しておく + //printf("prog_num = %I64d / %I64d\n", prog_num, prog_base); + + // ファイルごとにブロックの CRC-32 を検証する + memset(buf, 0, io_size); + j = 0; + while (j < source_num){ + last_file = s_blk[j].file; + src_num = (int)((files[last_file].size + (__int64)block_size - 1) / block_size); + i = j + src_num - 1; // 末尾ブロックの番号 + if (s_blk[i].size < block_size){ // 残りを 0 でパディングする + len = block_size - s_blk[i].size; + while (len > io_size){ + len -= io_size; + s_blk[i].crc = crc_update(s_blk[i].crc, buf, io_size); + } + s_blk[i].crc = crc_update(s_blk[i].crc, buf, len); + } + memset(hash, 0, 16); + for (i = 0; i < src_num; i++) // XOR して 16バイトに減らす + ((unsigned int *)hash)[i & 3] ^= s_blk[j + i].crc ^ 0xFFFFFFFF; + if (memcmp(files[last_file].hash, hash, 16) != 0){ + printf("checksum mismatch, input file %d\n", last_file); + err = 1; + goto error_end; + } + j += src_num; + } + + //printf("io_size = %d, block_size = %d\n", io_size, block_size); + if (io_size < block_size){ // 1回で書き込みが終わらなかったなら + if (GetTickCount() - time_last >= UPDATE_TIME){ // キャンセルを受け付ける + if (cancel_progress()){ + err = 2; + goto error_end; + } + } + +#ifdef TIMER +time_start = GetTickCount(); +#endif + // 最後に Recovery Slice packet のヘッダーを書き込む + for (i = 0; i < parity_num; i++){ + Phmd5End(&(md_ptr[i])); + memcpy(header_buf + 16, md_ptr[i].hash, 16); + j = first_num + i; // 最初のパリティ・ブロック番号の分だけ足す + memcpy(header_buf + 64, &j, 4); // Recovery Slice の番号を書き込む + // リカバリ・ファイルに書き込む + if (file_write_data(rcv_hFile[p_blk[i].file], p_blk[i].off - 68, header_buf, 68)){ // ヘッダーのサイズ分だけずらす + printf("file_write_data, packet header\n"); + err = 1; + goto error_end; + } + } +#ifdef TIMER +time_write += GetTickCount() - time_start; +#endif + } + +#ifdef TIMER +printf("read %d.%03d sec\n", time_read / 1000, time_read % 1000); +printf("write %d.%03d sec\n", time_write / 1000, time_write % 1000); +#endif + +error_end: + InterlockedExchange(&(th->now), INT_MAX / 2); // サブ・スレッドの計算を中断する + for (j = 0; j < cpu_num; j++){ + if (hSub[j]){ // サブ・スレッドを終了させる + SetEvent(hRun[j]); + WaitForSingleObject(hSub[j], INFINITE); + CloseHandle(hSub[j]); + } + } + if (md_ptr) + free(md_ptr); + if (hFile) + CloseHandle(hFile); + if (buf) + _aligned_free(buf); + return err; +} + +int encode_method3( // パリティ・ブロックを全て保持して、一度に書き込む場合 + wchar_t *file_path, + wchar_t *recovery_path, // 作業用 + int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限 + int block_distri, // パリティ・ブロックの分配方法 (3-bit目は番号の付け方) + int packet_num, // 共通パケットの数 + unsigned char *common_buf, // 共通パケットのバッファー + int common_size, // 共通パケットのバッファー・サイズ + unsigned char *footer_buf, // 末尾パケットのバッファー + int footer_size, // 末尾パケットのバッファー・サイズ + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_c *files, // ソース・ファイルの情報 + source_ctx_c *s_blk, // ソース・ブロックの情報 + unsigned short *constant) +{ + unsigned char *buf = NULL, *p_buf; + unsigned short *factor1; + int err = 0, i, j, last_file, source_off, read_num, packet_off; + int src_num, chunk_num; + unsigned int unit_size, len; + unsigned int time_last, prog_write; + __int64 prog_num = 0, prog_base; + size_t mem_size; + HANDLE hFile = NULL; + HANDLE hSub[MAX_CPU], hRun[MAX_CPU], hEnd[MAX_CPU]; + RS_TH th[1]; + PHMD5 file_md_ctx, blk_md_ctx; + + memset(hSub, 0, sizeof(HANDLE) * MAX_CPU); + factor1 = constant + source_num; + unit_size = (block_size + HASH_SIZE + (sse_unit - 1)) & ~(sse_unit - 1); // チェックサムの分だけ増やす + + // 作業バッファーを確保する + read_num = read_block_num(parity_num, 0, 1, sse_unit); // ソース・ブロックを何個読み込むか + if (read_num == 0){ +#ifdef TIMER + printf("cannot keep enough blocks, use another method\n"); +#endif + return -2; // スライスを分割して処理しないと無理 + } + print_progress_text(0, "Creating recovery slice"); + //read_num = (read_num + 1) / 2 + 1; // 2分割の実験用 + //read_num = (read_num + 2) / 3 + 1; // 3分割の実験用 + mem_size = (size_t)(read_num + parity_num) * unit_size; + buf = _aligned_malloc(mem_size, sse_unit); + if (buf == NULL){ + printf("malloc, %Id\n", mem_size); + err = 1; + goto error_end; + } + p_buf = buf + (size_t)unit_size * read_num; // パリティ・ブロックを記録する領域 + prog_write = source_num >> 5; // 計算で 97%、書き込みで 3% ぐらい + if (prog_write == 0) + prog_write = 1; + prog_base = (__int64)(source_num + prog_write) * parity_num; // ブロックの合計掛け算個数 + 書き込み回数 + len = try_cache_blocking(unit_size); + chunk_num = (unit_size + len - 1) / len; +#ifdef TIMER + printf("\n read some source blocks, and keep all parity blocks\n"); + printf("buffer size = %Id MB, read_num = %d, round = %d\n", mem_size >> 20, read_num, (source_num + read_num - 1) / read_num); + printf("cache: limit size = %d, chunk_size = %d, split = %d\n", cpu_cache & 0x7FFF8000, len, chunk_num); + printf("prog_base = %I64d, unit_size = %d\n", prog_base, unit_size); +#endif + + // マルチ・スレッドの準備をする + th->mat = constant; + th->buf = p_buf; + th->size = unit_size; + th->off = len; // キャッシュの最適化を試みる + for (j = 0; j < cpu_num; j++){ // サブ・スレッドごとに + hRun[j] = CreateEvent(NULL, FALSE, FALSE, NULL); // Auto Reset にする + if (hRun[j] == NULL){ + print_win32_err(); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + hEnd[j] = CreateEvent(NULL, TRUE, FALSE, NULL); + if (hEnd[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + // サブ・スレッドを起動する + th->run = hRun[j]; + th->end = hEnd[j]; + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する + hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_encode3, (LPVOID)th, 0, NULL); + if (hSub[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + CloseHandle(hEnd[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + WaitForSingleObject(hEnd[j], INFINITE); // 設定終了の合図を待つ (リセットしない) + } + // IO が延滞しないように、サブ・スレッド一つの優先度を下げる + SetThreadPriority(hSub[0], THREAD_PRIORITY_BELOW_NORMAL); + + // 何回かに別けてソース・ブロックを読み込んで、パリティ・ブロックを少しずつ作成する + time_last = GetTickCount(); + wcscpy(file_path, base_dir); + last_file = -1; + source_off = 0; // 読み込み開始スライス番号 + while (source_off < source_num){ + if (read_num > source_num - source_off) + read_num = source_num - source_off; + th->size = 0; // 1st encode + th->off = source_off - 1; // まだ計算して無い印 + +#ifdef TIMER +time_start = GetTickCount(); +#endif + for (i = 0; i < read_num; i++){ // スライスを一個ずつ読み込んでメモリー上に配置していく + // ソース・ブロックを読み込む + if (s_blk[source_off + i].file != last_file){ // 別のファイルなら開く + if (hFile){ + CloseHandle(hFile); // 前のファイルを閉じる + hFile = NULL; + // チェックサム・パケットの MD5 を計算する + memcpy(&packet_off, files[last_file].hash + 8, 4); + memcpy(&len, files[last_file].hash + 12, 4); + //printf("Checksum[%d], off = %d, size = %d\n", last_file, packet_off, len); + Phmd5Begin(&blk_md_ctx); + Phmd5Process(&blk_md_ctx, common_buf + packet_off + 32, 32 + len); + Phmd5End(&blk_md_ctx); + memcpy(common_buf + packet_off + 16, blk_md_ctx.hash, 16); + // ファイルのハッシュ値の計算を終える + Phmd5End(&file_md_ctx); + memcpy(&packet_off, files[last_file].hash, 4); // ハッシュ値の位置 = off + 64 + 16 + memcpy(&len, files[last_file].hash + 4, 4); + //printf("File[%d], off = %d, size = %d\n", last_file, packet_off, len); + // ファイルのハッシュ値を書き込んでから、パケットの MD5 を計算する + memcpy(common_buf + packet_off + 64 + 16, file_md_ctx.hash, 16); + Phmd5Begin(&file_md_ctx); + Phmd5Process(&file_md_ctx, common_buf + packet_off + 32, 32 + len); + Phmd5End(&file_md_ctx); + memcpy(common_buf + packet_off + 16, file_md_ctx.hash, 16); + } + last_file = s_blk[source_off + i].file; + wcscpy(file_path + base_len, list_buf + files[last_file].name); + // 1-pass方式なら、断片化しないので FILE_FLAG_SEQUENTIAL_SCAN を付けた方がいいかも + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + hFile = NULL; + printf_cp("cannot open file, %s\n", list_buf + files[last_file].name); + err = 1; + goto error_end; + } + // ファイルのハッシュ値の計算を始める + Phmd5Begin(&file_md_ctx); + // チェックサムの位置 = off + 64 + 16 + memcpy(&packet_off, files[last_file].hash + 8, 4); + packet_off += 64 + 16; + } + // バッファーにソース・ファイルの内容を読み込む + len = s_blk[source_off + i].size; + if (!ReadFile(hFile, buf + (size_t)unit_size * i, len, &j, NULL) || (len != j)){ + print_win32_err(); + err = 1; + goto error_end; + } + if (len < block_size) + memset(buf + ((size_t)unit_size * i + len), 0, block_size - len); + // ファイルのハッシュ値を計算する + Phmd5Process(&file_md_ctx, buf + (size_t)unit_size * i, len); + // ソース・ブロックのチェックサムを計算する + len = crc_update(0xFFFFFFFF, buf + (size_t)unit_size * i, block_size) ^ 0xFFFFFFFF; // include pad + Phmd5Begin(&blk_md_ctx); + Phmd5Process(&blk_md_ctx, buf + (size_t)unit_size * i, block_size); + Phmd5End(&blk_md_ctx); + memcpy(common_buf + packet_off, blk_md_ctx.hash, 16); + memcpy(common_buf + packet_off + 16, &len, 4); + packet_off += 20; + checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + unit_size - HASH_SIZE), unit_size - HASH_SIZE); + + if (i + 1 < read_num){ // 最後のブロック以外なら + // サブ・スレッドの動作状況を調べる + j = WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, 0); + if ((j != WAIT_TIMEOUT) && (j != WAIT_FAILED)){ // 計算中でないなら + // 経過表示 + prog_num += parity_num; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + // 計算終了したブロックの次から計算を開始する + th->off += 1; + th->buf = buf + (size_t)unit_size * (th->off - source_off); + for (j = 0; j < parity_num; j++) + factor1[j] = galois_power(constant[th->off], first_num + j); // factor は定数行列の乗数になる + th->now = -1; // 初期値 - 1 + //_mm_sfence(); + for (j = 0; j < (cpu_num + 1) / 2; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + } + } + } + if (source_off + i == source_num){ // 最後のソース・ファイルを閉じる + CloseHandle(hFile); + hFile = NULL; + // チェックサム・パケットの MD5 を計算する + memcpy(&packet_off, files[last_file].hash + 8, 4); + memcpy(&len, files[last_file].hash + 12, 4); + //printf("Checksum[%d], off = %d, size = %d\n", last_file, packet_off, len); + Phmd5Begin(&blk_md_ctx); + Phmd5Process(&blk_md_ctx, common_buf + packet_off + 32, 32 + len); + Phmd5End(&blk_md_ctx); + memcpy(common_buf + packet_off + 16, blk_md_ctx.hash, 16); + // ファイルのハッシュ値の計算を終える + Phmd5End(&file_md_ctx); + memcpy(&packet_off, files[last_file].hash, 4); // ハッシュ値の位置 = off + 64 + 16 + memcpy(&len, files[last_file].hash + 4, 4); + //printf("File[%d], off = %d, size = %d\n", last_file, packet_off, len); + // ファイルのハッシュ値を書き込んでから、パケットの MD5 を計算する + memcpy(common_buf + packet_off + 64 + 16, file_md_ctx.hash, 16); + Phmd5Begin(&file_md_ctx); + Phmd5Process(&file_md_ctx, common_buf + packet_off + 32, 32 + len); + Phmd5End(&file_md_ctx); + memcpy(common_buf + packet_off + 16, file_md_ctx.hash, 16); + } +#ifdef TIMER +time_read += GetTickCount() - time_start; +#endif + + WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, INFINITE); // サブ・スレッドの計算終了の合図を待つ + th->off += 1; // 計算を開始するソース・ブロックの番号 + if (th->off == 0) // エラーや実験時以外は th->off は 0 にならない + memset(p_buf, 0, (size_t)unit_size * parity_num); +#ifdef TIMER + j = ((th->off - source_off) * 1000) / read_num; + printf("partial encode = %d / %d (%d.%d%%), source_off = %d\n", th->off - source_off, read_num, j / 10, j % 10, source_off); + // ここまでのパリティ・ブロックのチェックサムを検証する +/* if (th->off - source_off > 0){ + __declspec( align(16) ) unsigned char hash[HASH_SIZE]; + for (j = 0; j < parity_num; j++){ + checksum16_return(p_buf + (size_t)unit_size * j, hash, unit_size - HASH_SIZE); + if (memcmp(p_buf + ((size_t)unit_size * j + unit_size - HASH_SIZE), hash, HASH_SIZE) != 0){ + printf("checksum mismatch, recovery slice %d after 1st encode\n", j); + err = 1; + goto error_end; + } + galois_altmap_change(p_buf + (size_t)unit_size * j, unit_size); + } + }*/ +#endif + + // スレッドごとにパリティ・ブロックを計算する + src_num = read_num - (th->off - source_off); // 一度に処理する量 (src_num > 0) + th->buf = buf + (size_t)unit_size * (th->off - source_off); + // th->off はソース・ブロックの番号 + th->size = src_num; + th->now = -1; // 初期値 - 1 + //_mm_sfence(); + for (j = 0; j < cpu_num; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + + // サブ・スレッドの計算終了の合図を UPDATE_TIME だけ待ちながら、経過表示する + while (WaitForMultipleObjects(cpu_num, hEnd, TRUE, UPDATE_TIME) == WAIT_TIMEOUT){ + // th-now が最高値なので、計算が終わってるのは th-now - cpu_num 個となる + j = th->now - cpu_num; + if (j < 0) + j = 0; + j /= chunk_num; // chunk数で割ってブロック数にする + // 経過表示(UPDATE_TIME 時間待った場合なので、必ず経過してるはず) + if (print_progress((int)(((prog_num + src_num * j) * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + + // 経過表示 + prog_num += src_num * parity_num; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + + source_off += read_num; + } + //printf("\nprog_num = %I64d / %I64d\n", prog_num, prog_base); + +#ifdef TIMER +time_start = GetTickCount(); +#endif + memcpy(common_buf + common_size, common_buf, common_size); // 後の半分に前半のをコピーする + // 最後にパリティ・ブロックのチェックサムを検証して、リカバリ・ファイルに書き込む + err = create_recovery_file_1pass(file_path, recovery_path, packet_limit, block_distri, + packet_num, common_buf, common_size, footer_buf, footer_size, rcv_hFile, p_buf, unit_size); +#ifdef TIMER +time_write = GetTickCount() - time_start; +#endif + +#ifdef TIMER +printf("read %d.%03d sec\n", time_read / 1000, time_read % 1000); +printf("write %d.%03d sec\n", time_write / 1000, time_write % 1000); +#endif + +error_end: + InterlockedExchange(&(th->now), INT_MAX / 2); // サブ・スレッドの計算を中断する + for (j = 0; j < cpu_num; j++){ + if (hSub[j]){ // サブ・スレッドを終了させる + SetEvent(hRun[j]); + WaitForSingleObject(hSub[j], INFINITE); + CloseHandle(hSub[j]); + } + } + if (hFile) + CloseHandle(hFile); + if (buf) + _aligned_free(buf); + return err; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int encode_method4( // 全てのブロックを断片的に保持する場合 (GPU対応) + wchar_t *file_path, + unsigned char *header_buf, // Recovery Slice packet のパケット・ヘッダー + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_c *files, // ソース・ファイルの情報 + source_ctx_c *s_blk, // ソース・ブロックの情報 + parity_ctx_c *p_blk, // パリティ・ブロックの情報 + unsigned short *constant) // 複数ブロック分の領域を確保しておく? +{ + unsigned char *buf = NULL, *p_buf, *work_buf, *hash; + unsigned short *factor1; + int err = 0, i, j, last_file; + int cpu_num1, cover_max, cover_from, cover_num; + unsigned int io_size, unit_size, len, block_off; + unsigned int time_last, prog_write; + __int64 file_off, prog_num = 0, prog_base; + HANDLE hFile = NULL; + HANDLE hSub[MAX_CPU], hRun[MAX_CPU], hEnd[MAX_CPU]; + RS_TH th[1]; + PHMD5 md_ctx, *md_ptr = NULL; + + memset(hSub, 0, sizeof(HANDLE) * MAX_CPU); + factor1 = constant + source_num; + cpu_num1 = cpu_num; // 最後のスレッドを GPU 管理用にする + if (cpu_num == 1) + cpu_num1++; + + // 作業バッファーを確保する(GPU の作業領域として2個の余裕を見ておく) + // part_num を使わず、全てのブロックを保持する所がencode_method2と異なることに注意! + io_size = get_io_size(source_num + parity_num + 2, NULL, 1, MEM_UNIT); + //io_size = (((io_size + 1) / 2 + HASH_SIZE + (MEM_UNIT - 1)) & ~(MEM_UNIT - 1)) - HASH_SIZE; // 2分割の実験用 + //io_size = (((io_size + 2) / 3 + HASH_SIZE + (MEM_UNIT - 1)) & ~(MEM_UNIT - 1)) - HASH_SIZE; // 3分割の実験用 + unit_size = io_size + HASH_SIZE; // チェックサムの分だけ増やす + file_off = (source_num + parity_num) * (size_t)unit_size + HASH_SIZE + + (source_num * sizeof(unsigned short) * cpu_num1); + buf = _aligned_malloc((size_t)file_off, MEM_UNIT); // GPU 用の境界 + if (buf == NULL){ + printf("malloc, %I64d\n", file_off); + err = 1; + goto error_end; + } + p_buf = buf + (size_t)unit_size * source_num; // パリティ・ブロックを記録する領域 + hash = p_buf + (size_t)unit_size * parity_num; + prog_base = (block_size + io_size - 1) / io_size; + prog_write = source_num >> 5; // 計算で 97%、書き込みで 3% ぐらい + if (prog_write == 0) + prog_write = 1; + prog_base *= (__int64)(source_num + prog_write) * parity_num; // 全体の断片の個数 +#ifdef TIMER + printf("\n read all source blocks, and keep all parity blocks (GPU)\n"); + printf("buffer size = %I64d MB, io_size = %d, split = %d\n", file_off >> 20, io_size, (block_size + io_size - 1) / io_size); +#endif + + if (io_size < block_size){ // スライスが分割される場合だけ、途中までのハッシュ値を保持する + block_off = sizeof(PHMD5) * parity_num; + md_ptr = malloc(block_off); + if (md_ptr == NULL){ + printf("malloc, %d\n", block_off); + err = 1; + goto error_end; + } + for (i = 0; i < parity_num; i++){ + Phmd5Begin(&(md_ptr[i])); + j = first_num + i; // 最初の番号の分だけ足す + memcpy(header_buf + 64, &j, 4); // Recovery Slice の番号を書き込む + Phmd5Process(&(md_ptr[i]), header_buf + 32, 36); + } + } + + // OpenCL の初期化 + cover_max = source_num; + len = 0; + i = init_OpenCL(unit_size, &cover_max, &len); + if (i != 0){ + if (i != 3) // GPU が見つからなかった場合はエラー表示しない + printf("init_OpenCL, %d, %d\n", i & 0xFF, i >> 8); + i = free_OpenCL(); + if (i != 0) + printf("free_OpenCL, %d, %d", i & 0xFF, i >> 8); + OpenCL_method = 0; // GPU を使わない設定にする + // GPU を使わずに計算を続行する場合は以下をコメントアウト + err = -2; // CPU だけの方式に切り替える + goto error_end; + } + if (len == 0) // GPUがキャッシュを使わない時だけ、CPU独自にキャッシュの最適化を試みる + len = try_cache_blocking(unit_size); +#ifdef TIMER + printf("cache: limit size = %d, chunk_size = %d, split = %d\n", cpu_cache & 0x7FFF8000, len, (unit_size + len - 1) / len); + printf("prog_base = %I64d, unit_size = %d, method = %d, cover_max = %d\n", prog_base, unit_size, OpenCL_method, cover_max); +#endif + + // マルチ・スレッドの準備をする + th->mat = constant; + th->buf = p_buf; + th->size = unit_size; + th->count = source_num; + th->off = len; // chunk size + for (j = 0; j < cpu_num1; j++){ // サブ・スレッドごとに + hRun[j] = CreateEvent(NULL, FALSE, FALSE, NULL); // Auto Reset にする + if (hRun[j] == NULL){ + print_win32_err(); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + hEnd[j] = CreateEvent(NULL, TRUE, FALSE, NULL); + if (hEnd[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + // サブ・スレッドを起動する + th->run = hRun[j]; + th->end = hEnd[j]; + th->now = j; // スレッド番号 + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する + if ((j == cpu_num1 - 1) && (OpenCL_method != 0)){ // 最後のスレッドを GPU 管理用にする + hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_encode_gpu, (LPVOID)th, 0, NULL); + } else { + hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_encode_each, (LPVOID)th, 0, NULL); + } + if (hSub[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + CloseHandle(hEnd[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + WaitForSingleObject(hEnd[j], INFINITE); // 設定終了の合図を待つ (リセットしない) + } + // IO が延滞しないように、サブ・スレッド一つの優先度を下げる + SetThreadPriority(hSub[0], THREAD_PRIORITY_BELOW_NORMAL); + + // ソース・ブロック断片を読み込んで、パリティ・ブロック断片を作成する + time_last = GetTickCount(); + wcscpy(file_path, base_dir); + block_off = 0; + while (block_off < block_size){ + th->size = 0xFFFFFFFF; // 1st encode + th->off = -1; // まだ計算して無い印 + + // ソース・ブロックを読み込む +#ifdef TIMER +read_count = 0; +skip_count = 0; +time_start = GetTickCount(); +#endif + last_file = -1; + for (i = 0; i < source_num; i++){ + if (s_blk[i].file != last_file){ // 別のファイルなら開く + last_file = s_blk[i].file; + if (hFile){ + CloseHandle(hFile); // 前のファイルを閉じる + hFile = NULL; + } + wcscpy(file_path + base_len, list_buf + files[last_file].name); + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + hFile = NULL; + printf_cp("cannot open file, %s\n", list_buf + files[last_file].name); + err = 1; + goto error_end; + } + file_off = block_off; + } else { // 同じファイルならブロック・サイズ分ずらす + file_off += block_size; + } + if (s_blk[i].size > block_off){ // バッファーにソース・ファイルの内容を読み込む + len = s_blk[i].size - block_off; + if (len > io_size) + len = io_size; + if (file_read_data(hFile, file_off, buf + (size_t)unit_size * i, len)){ + printf("file_read_data, input slice %d\n", i); + err = 1; + goto error_end; + } + if (len < io_size) + memset(buf + ((size_t)unit_size * i + len), 0, io_size - len); + // ソース・ブロックのチェックサムを計算する + if (block_off == 0) + s_blk[i].crc = 0xFFFFFFFF; + s_blk[i].crc = crc_update(s_blk[i].crc, buf + (size_t)unit_size * i, len); // without pad + checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + io_size), io_size); +#ifdef TIMER +read_count++; +#endif + + if (i + 1 < source_num){ // 最後のブロック以外なら + // サブ・スレッドの動作状況を調べる + j = WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, 0); + if ((j != WAIT_TIMEOUT) && (j != WAIT_FAILED)){ // 計算中でないなら + // 経過表示 + prog_num += parity_num; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + // 計算終了したブロックの次から計算を開始する + th->off += 1; + if (th->off > 0){ // バッファーに読み込んだ時だけ計算する + while (s_blk[th->off].size <= block_off){ + prog_num += parity_num; + th->off += 1; +#ifdef TIMER +skip_count++; +#endif + } + } + th->buf = buf + (size_t)unit_size * th->off; + for (j = 0; j < parity_num; j++) + factor1[j] = galois_power(constant[th->off], first_num + j); // factor は定数行列の乗数になる + th->now = -1; // 初期値 - 1 + //_mm_sfence(); + for (j = 0; j < (cpu_num + 1) / 2; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + } + } + } else { + memset(buf + (size_t)unit_size * i, 0, unit_size); + } + } + // 最後のソース・ファイルを閉じる + CloseHandle(hFile); + hFile = NULL; +#ifdef TIMER +time_read += GetTickCount() - time_start; +#endif + + WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, INFINITE); // サブ・スレッドの計算終了の合図を待つ + th->size = 0; // 2nd encode + th->off += 1; // 計算を開始するソース・ブロックの番号 + if (th->off > 0){ + while (s_blk[th->off].size <= block_off){ // 計算不要なソース・ブロックはとばす + prog_num += parity_num; + th->off += 1; +#ifdef TIMER +skip_count++; +#endif + } + } else { // エラーや実験時以外は th->off は 0 にならない + memset(p_buf, 0, (size_t)unit_size * parity_num); + } +#ifdef TIMER + j = (th->off * 1000) / source_num; + printf("partial encode = %d (%d.%d%%), read = %d, skip = %d\n", th->off, j / 10, j % 10, read_count, skip_count); +#endif + + // リカバリ・ファイルに書き込むサイズ + if (block_size - block_off < io_size){ + len = block_size - block_off; + } else { + len = io_size; + } + + // VRAM のサイズに応じて分割する + cover_from = th->off; + i = (source_num - cover_from + cover_max - 1) / cover_max; // 何回に分けて処理するか + cover_num = (source_num - cover_from + i - 1) / i; // 一度に処理する量を平均化する + //printf("cover range = %d, cover_num = %d\n", source_num - cover_from, cover_num); + while (cover_from < source_num){ + // ソース・ブロックを何個ずつ処理するか + if (cover_from + cover_num > source_num) + cover_num = source_num - cover_from; + //printf("cover_from = %d, cover_num = %d\n", cover_from, cover_num); + + // GPU と CPU がスレッドごとにパリティ・ブロックを計算する + th->buf = buf + (size_t)unit_size * cover_from; + th->off = cover_from; // ソース・ブロックの番号にする + th->count = cover_num; + th->now = -1; // 初期値 - 1 + //_mm_sfence(); + for (j = 0; j < cpu_num1; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + + // サブ・スレッドの計算終了の合図を UPDATE_TIME だけ待ちながら、経過表示する + while (WaitForMultipleObjects(cpu_num1, hEnd, TRUE, UPDATE_TIME) == WAIT_TIMEOUT){ + // th-now が最高値なので、計算が終わってるのは th-now - cpu_num1 個となる + j = th->now - cpu_num1; + if (j < 0) + j = 0; + // 経過表示(UPDATE_TIME 時間待った場合なので、必ず経過してるはず) + if (print_progress((int)(((prog_num + cover_num * j) * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + if (th->size != 0){ // エラー発生 + i = th->size; + printf("error, gpu-thread, %d, %d\n", i & 0xFF, i >> 8); + err = 1; + goto error_end; + } + + // 経過表示 + prog_num += cover_num * parity_num; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + + cover_from += cover_num; + } + +#ifdef TIMER +time_start = GetTickCount(); +#endif + // パリティ・ブロックを書き込む + work_buf = p_buf; + for (i = 0; i < parity_num; i++){ + // パリティ・ブロックのチェックサムを検証する + checksum16_return(work_buf, hash, io_size); + if (memcmp(work_buf + io_size, hash, HASH_SIZE) != 0){ + printf("checksum mismatch, recovery slice %d\n", i); + err = 1; + goto error_end; + } + // ハッシュ値を計算して、リカバリ・ファイルに書き込む + if (io_size >= block_size){ // 1回で書き込みが終わるなら + Phmd5Begin(&md_ctx); + j = first_num + i; // 最初の番号の分だけ足す + memcpy(header_buf + 64, &j, 4); // Recovery Slice の番号を書き込む + Phmd5Process(&md_ctx, header_buf + 32, 36); + Phmd5Process(&md_ctx, work_buf, len); + Phmd5End(&md_ctx); + memcpy(header_buf + 16, md_ctx.hash, 16); + // ヘッダーを書き込む + if (file_write_data(rcv_hFile[p_blk[i].file], p_blk[i].off + block_off - 68, header_buf, 68)){ + printf("file_write_data, recovery slice %d\n", i); + err = 1; + goto error_end; + } + } else { + Phmd5Process(&(md_ptr[i]), work_buf, len); + } + //printf("%d, buf = %p, size = %u, off = %I64d\n", i, work_buf, len, p_blk[i].off + block_off); + if (file_write_data(rcv_hFile[p_blk[i].file], p_blk[i].off + block_off, work_buf, len)){ + printf("file_write_data, recovery slice %d\n", i); + err = 1; + goto error_end; + } + work_buf += unit_size; + + // 経過表示 + prog_num += prog_write; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + } +#ifdef TIMER +time_write += GetTickCount() - time_start; +#endif + + block_off += io_size; + } + print_progress_done(); // 改行して行の先頭に戻しておく + //printf("prog_num = %I64d / %I64d\n", prog_num, prog_base); + + // ファイルごとにブロックの CRC-32 を検証する + memset(buf, 0, io_size); + j = 0; + while (j < source_num){ + last_file = s_blk[j].file; + cover_num = (int)((files[last_file].size + (__int64)block_size - 1) / block_size); + i = j + cover_num - 1; // 末尾ブロックの番号 + if (s_blk[i].size < block_size){ // 残りを 0 でパディングする + len = block_size - s_blk[i].size; + while (len > io_size){ + len -= io_size; + s_blk[i].crc = crc_update(s_blk[i].crc, buf, io_size); + } + s_blk[i].crc = crc_update(s_blk[i].crc, buf, len); + } + memset(hash, 0, 16); + for (i = 0; i < cover_num; i++) // XOR して 16バイトに減らす + ((unsigned int *)hash)[i & 3] ^= s_blk[j + i].crc ^ 0xFFFFFFFF; + if (memcmp(files[last_file].hash, hash, 16) != 0){ + printf("checksum mismatch, input file %d\n", last_file); + err = 1; + goto error_end; + } + j += cover_num; + } + + //printf("io_size = %d, block_size = %d\n", io_size, block_size); + if (io_size < block_size){ // 1回で書き込みが終わらなかったなら + if (GetTickCount() - time_last >= UPDATE_TIME){ // キャンセルを受け付ける + if (cancel_progress()){ + err = 2; + goto error_end; + } + } + +#ifdef TIMER +time_start = GetTickCount(); +#endif + // 最後に Recovery Slice packet のヘッダーを書き込む + for (i = 0; i < parity_num; i++){ + Phmd5End(&(md_ptr[i])); + memcpy(header_buf + 16, md_ptr[i].hash, 16); + j = first_num + i; // 最初のパリティ・ブロック番号の分だけ足す + memcpy(header_buf + 64, &j, 4); // Recovery Slice の番号を書き込む + // リカバリ・ファイルに書き込む + if (file_write_data(rcv_hFile[p_blk[i].file], p_blk[i].off - 68, header_buf, 68)){ // ヘッダーのサイズ分だけずらす + printf("file_write_data, packet header\n"); + err = 1; + goto error_end; + } + } +#ifdef TIMER +time_write += GetTickCount() - time_start; +#endif + } + +#ifdef TIMER +printf("read %d.%03d sec\n", time_read / 1000, time_read % 1000); +printf("write %d.%03d sec\n", time_write / 1000, time_write % 1000); +#endif + info_OpenCL(buf, MEM_UNIT); // デバイス情報を表示する + +error_end: + InterlockedExchange(&(th->now), INT_MAX / 2); // サブ・スレッドの計算を中断する + for (j = 0; j < cpu_num1; j++){ + if (hSub[j]){ // サブ・スレッドを終了させる + SetEvent(hRun[j]); + WaitForSingleObject(hSub[j], INFINITE); + CloseHandle(hSub[j]); + } + } + if (md_ptr) + free(md_ptr); + if (hFile) + CloseHandle(hFile); + if (buf) + _aligned_free(buf); + i = free_OpenCL(); + if (i != 0) + printf("free_OpenCL, %d, %d", i & 0xFF, i >> 8); + return err; +} + +int encode_method5( // ソース・ブロックの一部とパリティ・ブロックを保持する場合 (GPU対応) + wchar_t *file_path, + wchar_t *recovery_path, // 作業用 + int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限 + int block_distri, // パリティ・ブロックの分配方法 (3-bit目は番号の付け方) + int packet_num, // 共通パケットの数 + unsigned char *common_buf, // 共通パケットのバッファー + int common_size, // 共通パケットのバッファー・サイズ + unsigned char *footer_buf, // 末尾パケットのバッファー + int footer_size, // 末尾パケットのバッファー・サイズ + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_c *files, // ソース・ファイルの情報 + source_ctx_c *s_blk, // ソース・ブロックの情報 + unsigned short *constant) +{ + unsigned char *buf = NULL, *p_buf; + unsigned short *factor1; + int err = 0, i, j, last_file, source_off, read_num, packet_off; + int cpu_num1, cover_max, cover_from, cover_num; + unsigned int unit_size, len; + unsigned int time_last, prog_write; + __int64 prog_num = 0, prog_base; + size_t mem_size; + HANDLE hFile = NULL; + HANDLE hSub[MAX_CPU], hRun[MAX_CPU], hEnd[MAX_CPU]; + RS_TH th[1]; + PHMD5 file_md_ctx, blk_md_ctx; + + memset(hSub, 0, sizeof(HANDLE) * MAX_CPU); + factor1 = constant + source_num; + unit_size = (block_size + HASH_SIZE + (MEM_UNIT - 1)) & ~(MEM_UNIT - 1); // MEM_UNIT の倍数にする + cpu_num1 = cpu_num; // 最後のスレッドを GPU 管理用にする + if (cpu_num == 1) + cpu_num1++; + + // 作業バッファーを確保する(GPU の作業領域として2個の余裕を見ておく) + read_num = read_block_num(parity_num, 2, 1, MEM_UNIT); // ソース・ブロックを何個読み込むか + if (read_num == 0){ +#ifdef TIMER + printf("cannot keep enough blocks, use another method\n"); +#endif + return -4; // スライスを分割して処理しないと無理 + } + //read_num = (read_num + 1) / 2 + 1; // 2分割の実験用 + //read_num = (read_num + 2) / 3 + 1; // 3分割の実験用 + mem_size = (size_t)(read_num + parity_num) * unit_size + + (read_num * sizeof(unsigned short) * cpu_num1); + buf = _aligned_malloc(mem_size, MEM_UNIT); // GPU 用の境界 + if (buf == NULL){ + printf("malloc, %Id\n", mem_size); + err = 1; + goto error_end; + } + p_buf = buf + (size_t)unit_size * read_num; // パリティ・ブロックを記録する領域 + prog_write = source_num >> 5; // 計算で 97%、書き込みで 3% ぐらい + if (prog_write == 0) + prog_write = 1; + prog_base = (__int64)(source_num + prog_write) * parity_num; // ブロックの合計掛け算個数 + 書き込み回数 + + // OpenCL の初期化 + cover_max = read_num; // 読み込める分だけにする + len = 0; + i = init_OpenCL(unit_size, &cover_max, &len); + if (i != 0){ + if (i != 3) // GPU が見つからなかった場合はエラー表示しない + printf("init_OpenCL, %d, %d\n", i & 0xFF, i >> 8); + i = free_OpenCL(); + if (i != 0) + printf("free_OpenCL, %d, %d", i & 0xFF, i >> 8); + OpenCL_method = 0; // GPU を使わない設定にする + // GPU を使わずに計算を続行する場合は以下をコメントアウト + err = -3; // CPU だけの方式に切り替える + goto error_end; + } + if (len == 0) // GPUがキャッシュを使わない時だけ、CPU独自にキャッシュの最適化を試みる + len = try_cache_blocking(unit_size); + print_progress_text(0, "Creating recovery slice"); +#ifdef TIMER + printf("\n read some source blocks, and keep all parity blocks (GPU)\n"); + printf("buffer size = %Id MB, read_num = %d, round = %d\n", mem_size >> 20, read_num, (source_num + read_num - 1) / read_num); + printf("cache: limit size = %d, chunk_size = %d, split = %d\n", cpu_cache & 0x7FFF8000, len, (unit_size + len - 1) / len); + printf("prog_base = %I64d, unit_size = %d, method = %d, cover_max = %d\n", prog_base, unit_size, OpenCL_method, cover_max); +#endif + + // マルチ・スレッドの準備をする + th->mat = constant; + th->buf = p_buf; + th->size = unit_size; + th->count = read_num; + th->off = len; // chunk size + for (j = 0; j < cpu_num1; j++){ // サブ・スレッドごとに + hRun[j] = CreateEvent(NULL, FALSE, FALSE, NULL); // Auto Reset にする + if (hRun[j] == NULL){ + print_win32_err(); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + hEnd[j] = CreateEvent(NULL, TRUE, FALSE, NULL); + if (hEnd[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + // サブ・スレッドを起動する + th->run = hRun[j]; + th->end = hEnd[j]; + th->now = j; // スレッド番号 + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する + if ((j == cpu_num1 - 1) && (OpenCL_method != 0)){ // 最後のスレッドを GPU 管理用にする + hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_encode_gpu, (LPVOID)th, 0, NULL); + } else { + hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_encode_each, (LPVOID)th, 0, NULL); + } + if (hSub[j] == NULL){ + print_win32_err(); + CloseHandle(hRun[j]); + CloseHandle(hEnd[j]); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + WaitForSingleObject(hEnd[j], INFINITE); // 設定終了の合図を待つ (リセットしない) + } + // IO が延滞しないように、サブ・スレッド一つの優先度を下げる + SetThreadPriority(hSub[0], THREAD_PRIORITY_BELOW_NORMAL); + + // 何回かに別けてソース・ブロックを読み込んで、パリティ・ブロックを少しずつ作成する + time_last = GetTickCount(); + wcscpy(file_path, base_dir); + last_file = -1; + source_off = 0; // 読み込み開始スライス番号 + while (source_off < source_num){ + if (read_num > source_num - source_off) + read_num = source_num - source_off; + th->size = 0xFFFFFFFF; // 1st encode + th->off = source_off - 1; // まだ計算して無い印 + +#ifdef TIMER +time_start = GetTickCount(); +#endif + for (i = 0; i < read_num; i++){ // スライスを一個ずつ読み込んでメモリー上に配置していく + // ソース・ブロックを読み込む + if (s_blk[source_off + i].file != last_file){ // 別のファイルなら開く + if (hFile){ + CloseHandle(hFile); // 前のファイルを閉じる + hFile = NULL; + // チェックサム・パケットの MD5 を計算する + memcpy(&packet_off, files[last_file].hash + 8, 4); + memcpy(&len, files[last_file].hash + 12, 4); + //printf("Checksum[%d], off = %d, size = %d\n", last_file, packet_off, len); + Phmd5Begin(&blk_md_ctx); + Phmd5Process(&blk_md_ctx, common_buf + packet_off + 32, 32 + len); + Phmd5End(&blk_md_ctx); + memcpy(common_buf + packet_off + 16, blk_md_ctx.hash, 16); + // ファイルのハッシュ値の計算を終える + Phmd5End(&file_md_ctx); + memcpy(&packet_off, files[last_file].hash, 4); // ハッシュ値の位置 = off + 64 + 16 + memcpy(&len, files[last_file].hash + 4, 4); + //printf("File[%d], off = %d, size = %d\n", last_file, packet_off, len); + // ファイルのハッシュ値を書き込んでから、パケットの MD5 を計算する + memcpy(common_buf + packet_off + 64 + 16, file_md_ctx.hash, 16); + Phmd5Begin(&file_md_ctx); + Phmd5Process(&file_md_ctx, common_buf + packet_off + 32, 32 + len); + Phmd5End(&file_md_ctx); + memcpy(common_buf + packet_off + 16, file_md_ctx.hash, 16); + } + last_file = s_blk[source_off + i].file; + wcscpy(file_path + base_len, list_buf + files[last_file].name); + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + hFile = NULL; + printf_cp("cannot open file, %s\n", list_buf + files[last_file].name); + err = 1; + goto error_end; + } + // ファイルのハッシュ値の計算を始める + Phmd5Begin(&file_md_ctx); + // チェックサムの位置 = off + 64 + 16 + memcpy(&packet_off, files[last_file].hash + 8, 4); + packet_off += 64 + 16; + } + // バッファーにソース・ファイルの内容を読み込む + len = s_blk[source_off + i].size; + if (!ReadFile(hFile, buf + (size_t)unit_size * i, len, &j, NULL) || (len != j)){ + print_win32_err(); + err = 1; + goto error_end; + } + if (len < block_size) + memset(buf + ((size_t)unit_size * i + len), 0, block_size - len); + // ファイルのハッシュ値を計算する + Phmd5Process(&file_md_ctx, buf + (size_t)unit_size * i, len); + // ソース・ブロックのチェックサムを計算する + len = crc_update(0xFFFFFFFF, buf + (size_t)unit_size * i, block_size) ^ 0xFFFFFFFF; // include pad + Phmd5Begin(&blk_md_ctx); + Phmd5Process(&blk_md_ctx, buf + (size_t)unit_size * i, block_size); + Phmd5End(&blk_md_ctx); + memcpy(common_buf + packet_off, blk_md_ctx.hash, 16); + memcpy(common_buf + packet_off + 16, &len, 4); + packet_off += 20; + checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + unit_size - HASH_SIZE), unit_size - HASH_SIZE); + + if (i + 1 < read_num){ // 最後のブロック以外なら + // サブ・スレッドの動作状況を調べる + j = WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, 0); + if ((j != WAIT_TIMEOUT) && (j != WAIT_FAILED)){ // 計算中でないなら + // 経過表示 + prog_num += parity_num; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + // 計算終了したブロックの次から計算を開始する + th->off += 1; + th->buf = buf + (size_t)unit_size * (th->off - source_off); + for (j = 0; j < parity_num; j++) + factor1[j] = galois_power(constant[th->off], first_num + j); // factor は定数行列の乗数になる + th->now = -1; // 初期値 - 1 + //_mm_sfence(); + for (j = 0; j < (cpu_num + 1) / 2; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + } + } + } + if (source_off + i == source_num){ // 最後のソース・ファイルを閉じる + CloseHandle(hFile); + hFile = NULL; + // チェックサム・パケットの MD5 を計算する + memcpy(&packet_off, files[last_file].hash + 8, 4); + memcpy(&len, files[last_file].hash + 12, 4); + //printf("Checksum[%d], off = %d, size = %d\n", last_file, packet_off, len); + Phmd5Begin(&blk_md_ctx); + Phmd5Process(&blk_md_ctx, common_buf + packet_off + 32, 32 + len); + Phmd5End(&blk_md_ctx); + memcpy(common_buf + packet_off + 16, blk_md_ctx.hash, 16); + // ファイルのハッシュ値の計算を終える + Phmd5End(&file_md_ctx); + memcpy(&packet_off, files[last_file].hash, 4); // ハッシュ値の位置 = off + 64 + 16 + memcpy(&len, files[last_file].hash + 4, 4); + //printf("File[%d], off = %d, size = %d\n", last_file, packet_off, len); + // ファイルのハッシュ値を書き込んでから、パケットの MD5 を計算する + memcpy(common_buf + packet_off + 64 + 16, file_md_ctx.hash, 16); + Phmd5Begin(&file_md_ctx); + Phmd5Process(&file_md_ctx, common_buf + packet_off + 32, 32 + len); + Phmd5End(&file_md_ctx); + memcpy(common_buf + packet_off + 16, file_md_ctx.hash, 16); + } +#ifdef TIMER +time_read += GetTickCount() - time_start; +#endif + + WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, INFINITE); // サブ・スレッドの計算終了の合図を待つ + th->size = 0; // 2nd encode + th->off += 1; // 計算を開始するソース・ブロックの番号 + if (th->off == 0) // エラーや実験時以外は th->off は 0 にならない + memset(p_buf, 0, (size_t)unit_size * parity_num); +#ifdef TIMER + j = (th->off - source_off) * 1000 / read_num; + printf("partial encode = %d (%d.%d%%)\n", th->off - source_off, j / 10, j % 10); +#endif + + // VRAM のサイズに応じて分割する + cover_from = th->off - source_off; + i = (read_num - cover_from + cover_max - 1) / cover_max; // 何回に分けて処理するか + cover_num = (read_num - cover_from + i - 1) / i; // 一度に処理する量を平均化する + //printf("cover range = %d, cover_num = %d\n", read_num - cover_from, cover_num); + while (cover_from < read_num){ + // ソース・ブロックを何個ずつ処理するか + if (cover_from + cover_num > read_num) + cover_num = read_num - cover_from; + //printf("cover_from = %d, cover_num = %d\n", cover_from, cover_num); + + // GPU と CPU がスレッドごとにパリティ・ブロックを計算する + th->buf = buf + (size_t)unit_size * cover_from; + th->off = source_off + cover_from; // ソース・ブロックの番号にする + th->count = cover_num; + th->now = -1; // 初期値 - 1 + //_mm_sfence(); + for (j = 0; j < cpu_num1; j++){ + ResetEvent(hEnd[j]); // リセットしておく + SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる + } + + // サブ・スレッドの計算終了の合図を UPDATE_TIME だけ待ちながら、経過表示する + while (WaitForMultipleObjects(cpu_num1, hEnd, TRUE, UPDATE_TIME) == WAIT_TIMEOUT){ + // th-now が最高値なので、計算が終わってるのは th-now - cpu_num1 個となる + j = th->now - cpu_num1; + if (j < 0) + j = 0; + // 経過表示(UPDATE_TIME 時間待った場合なので、必ず経過してるはず) + if (print_progress((int)(((prog_num + cover_num * j) * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + if (th->size != 0){ // エラー発生 + i = th->size; + printf("error, gpu-thread, %d, %d\n", i & 0xFF, i >> 8); + err = 1; + goto error_end; + } + + // 経過表示 + prog_num += cover_num * parity_num; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + + cover_from += cover_num; + } + + source_off += read_num; + } + //printf("\nprog_num = %I64d / %I64d\n", prog_num, prog_base); + +#ifdef TIMER +time_start = GetTickCount(); +#endif + memcpy(common_buf + common_size, common_buf, common_size); // 後の半分に前半のをコピーする + // 最後にパリティ・ブロックのチェックサムを検証して、リカバリ・ファイルに書き込む + err = create_recovery_file_1pass(file_path, recovery_path, packet_limit, block_distri, + packet_num, common_buf, common_size, footer_buf, footer_size, rcv_hFile, p_buf, unit_size); +#ifdef TIMER +time_write = GetTickCount() - time_start; +#endif + +#ifdef TIMER +printf("read %d.%03d sec\n", time_read / 1000, time_read % 1000); +printf("write %d.%03d sec\n", time_write / 1000, time_write % 1000); +#endif + info_OpenCL(buf, MEM_UNIT); // デバイス情報を表示する + +error_end: + InterlockedExchange(&(th->now), INT_MAX / 2); // サブ・スレッドの計算を中断する + for (j = 0; j < cpu_num1; j++){ + if (hSub[j]){ // サブ・スレッドを終了させる + SetEvent(hRun[j]); + WaitForSingleObject(hSub[j], INFINITE); + CloseHandle(hSub[j]); + } + } + if (hFile) + CloseHandle(hFile); + if (buf) + _aligned_free(buf); + i = free_OpenCL(); + if (i != 0) + printf("free_OpenCL, %d, %d", i & 0xFF, i >> 8); + return err; +} + diff --git a/source/par2j/rs_encode.h b/source/par2j/rs_encode.h new file mode 100644 index 0000000..bee5dfc --- /dev/null +++ b/source/par2j/rs_encode.h @@ -0,0 +1,72 @@ +#ifndef _RS_ENCODE_H_ +#define _RS_ENCODE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +int encode_method1( // ソース・ブロックが一個だけの場合 + wchar_t *file_path, + unsigned char *header_buf, // Recovery Slice packet のパケット・ヘッダー + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_c *files, // ソース・ファイルの情報 + source_ctx_c *s_blk, // ソース・ブロックの情報 + parity_ctx_c *p_blk); // パリティ・ブロックの情報 + + +int encode_method2( // ソース・データを全て読み込む場合 + wchar_t *file_path, + unsigned char *header_buf, // Recovery Slice packet のパケット・ヘッダー + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_c *files, // ソース・ファイルの情報 + source_ctx_c *s_blk, // ソース・ブロックの情報 + parity_ctx_c *p_blk, // パリティ・ブロックの情報 + unsigned short *constant); + +int encode_method3( // パリティ・ブロックを全て保持して、一度に書き込む場合 + wchar_t *file_path, + wchar_t *recovery_path, // 作業用 + int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限 + int block_distri, // パリティ・ブロックの分配方法 (3-bit目は番号の付け方) + int packet_num, // 共通パケットの数 + unsigned char *common_buf, // 共通パケットのバッファー + int common_size, // 共通パケットのバッファー・サイズ + unsigned char *footer_buf, // 末尾パケットのバッファー + int footer_size, // 末尾パケットのバッファー・サイズ + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_c *files, // ソース・ファイルの情報 + source_ctx_c *s_blk, // ソース・ブロックの情報 + unsigned short *constant); + + +int encode_method4( // 全てのブロックを断片的に保持する場合 (GPU対応) + wchar_t *file_path, + unsigned char *header_buf, // Recovery Slice packet のパケット・ヘッダー + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_c *files, // ソース・ファイルの情報 + source_ctx_c *s_blk, // ソース・ブロックの情報 + parity_ctx_c *p_blk, // パリティ・ブロックの情報 + unsigned short *constant); // 複数ブロック分の領域を確保しておく? + +int encode_method5( // ソース・ブロックの一部とパリティ・ブロックを保持する場合 (GPU対応) + wchar_t *file_path, + wchar_t *recovery_path, // 作業用 + int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限 + int block_distri, // パリティ・ブロックの分配方法 (3-bit目は番号の付け方) + int packet_num, // 共通パケットの数 + unsigned char *common_buf, // 共通パケットのバッファー + int common_size, // 共通パケットのバッファー・サイズ + unsigned char *footer_buf, // 末尾パケットのバッファー + int footer_size, // 末尾パケットのバッファー・サイズ + HANDLE *rcv_hFile, // リカバリ・ファイルのハンドル + file_ctx_c *files, // ソース・ファイルの情報 + source_ctx_c *s_blk, // ソース・ブロックの情報 + unsigned short *constant); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par2j/search.c b/source/par2j/search.c new file mode 100644 index 0000000..a39be1e --- /dev/null +++ b/source/par2j/search.c @@ -0,0 +1,1243 @@ +// search.c +// Copyright : 2022-10-14 Yutaka Sawada +// License : GPL + +#ifndef _UNICODE +#define _UNICODE +#endif +#ifndef UNICODE +#define UNICODE +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 // Windows Vista or later +#endif + +#include + +#include + +#include "common2.h" +#include "crc.h" +#include "md5_crc.h" +#include "ini.h" +#include "json.h" +#include "search.h" + +#define MIN_PACKET_SIZE 68 + +// ファイル情報のファイル名の文字コードを変換しなおす +static int convert_filename(unsigned int new_CP, unsigned int old_CP, file_ctx_r *files) +{ + char ascii_buf[MAX_LEN * 3]; + wchar_t uni_buf[MAX_LEN]; + int i, j, len; + + // 全てのファイル名を変換可能かを先に確かめる + for (i = 0; i < file_num; i++){ + if (files[i].name <= 0) + continue; // ファイル名が取得できなかったファイルはとばす + // old_CP で変換されたユニコードから元のデータに戻す + if (utf16_to_cp(list_buf + files[i].name, ascii_buf, old_CP)) + return 1; + // new_CP で再度変換しなおす + if (cp_to_utf16(ascii_buf, uni_buf, new_CP)) + return 1; + } + + // 先に確認済みなので、変換エラーは発生しないはず + for (i = 0; i < file_num; i++){ + if (files[i].name <= 0) + continue; // ファイル名が取得できなかったファイルはとばす + // old_CP で変換されたユニコードから元のデータに戻す + utf16_to_cp(list_buf + files[i].name, ascii_buf, old_CP); + // new_CP で再度変換しなおす + cp_to_utf16(ascii_buf, uni_buf, new_CP); + // 変換前のファイル名をリストから取り除く + len = (int)wcslen(list_buf + files[i].name); + list_len = remove_file_path(list_buf, list_len, files[i].name); + for (j = 0; j < file_num; j++){ + if (files[j].name > files[i].name) + files[j].name -= (len + 1); // リスト上で後ろのやつをずらしていく + } + // 変換しなおしたファイル名を追加する + files[i].name = list_len; + if (add_file_path(uni_buf)) + return 2; + } + return 0; +} + +// UTF-8 で記録しない PAR2 client かどうか +static int non_utf8_client(char *creator) +{ + int version; + + if (strncmp(creator, "QuickPar ", 9) == 0){ + // QuickPar 0.9 + version = creator[9] - 48; + version *= 10; + version += creator[10] - 48; + if (version <= 9) + return 1; + } else if (strncmp(creator, "Created by phpar2 version ", 26) == 0){ + // Created by phpar2 version 1.3. + version = creator[26] - 48; + version *= 10; + version += creator[28] - 48; + if (version <= 13) + return 1; + } + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// リカバリ・ファイルを検索してファイル・リストに追加する +int search_recovery_files(void) +{ + wchar_t recovery_base[MAX_LEN], search_path[MAX_LEN], file_ext[EXT_LEN]; + wchar_t *tmp_p; + int len, dir_len, ext_len, l_max; + HANDLE hFind; + WIN32_FIND_DATA FindData; + + get_base_dir(recovery_file, recovery_base); + dir_len = (int)wcslen(recovery_base); + + // リカバリ・ファイルの名前の基 + get_base_filename(recovery_file, search_path, file_ext); + if (switch_v & 2){ // 追加検査なら同じディレクトリ内の全ファイル + wcscpy(search_path, recovery_base); + wcscat(search_path, L"*"); + if (file_ext[0] != 0) // 拡張子があったなら「*.拡張子」で検索する + wcscat(search_path, file_ext); + } else { + if (file_ext[0] != 0){ // 拡張子があったなら「.*拡張子」で検索する + file_ext[0] = '*'; + wcscat(search_path, L"."); + wcscat(search_path, file_ext); + file_ext[0] = '.'; + } else { + wcscat(search_path, L"*"); + } + } + ext_len = (int)wcslen(file_ext); + + l_max = ALLOC_LEN; + recv_len = 0; + recv_buf = (wchar_t *)malloc(l_max * 2); + if (recv_buf == NULL){ + printf("malloc, %d\n", l_max * 2); + return 1; + } + + // リカバリ・ファイルを検索する + hFind = FindFirstFile(search_path, &FindData); + if (hFind == INVALID_HANDLE_VALUE){ + print_win32_err(); + printf_cp("cannot find recovery file, %s\n", search_path); + return 1; + } + do { + if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0){ + len = (int)wcslen(FindData.cFileName); + if (dir_len + len >= MAX_LEN) + continue; // 長すぎるファイル名は無視する + if (file_ext[0] != 0){ // 拡張子が同じファイルだけ検査する + if ((len <= ext_len) || (_wcsicmp(FindData.cFileName + (len - ext_len), file_ext) != 0)) + continue; + } + if (recv_len + dir_len + len >= l_max){ // 領域が足りなくなるなら拡張する + l_max += ALLOC_LEN; + tmp_p = (wchar_t *)realloc(recv_buf, l_max * 2); + if (tmp_p == NULL){ + FindClose(hFind); + printf("realloc, %d\n", l_max * 2); + return 1; + } else { + recv_buf = tmp_p; + } + } + + // リストにコピーする + wcscpy(recv_buf + recv_len, recovery_base); + recv_len += dir_len; + wcscpy(recv_buf + recv_len, FindData.cFileName); + recv_len += len + 1; + recovery_num++; + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + // リカバリ・ファイルの拡張子が .par2 以外なら再検索する + if ((file_ext[0] != 0) && (_wcsicmp(file_ext, L".par2") != 0)){ + get_base_filename(recovery_file, search_path, file_ext); + wcscat(search_path, L"."); + wcscat(search_path, L"*"); + wcscat(search_path, L".par2"); // 「.*.par2」で検索する + hFind = FindFirstFile(search_path, &FindData); + if (hFind != INVALID_HANDLE_VALUE){ + do { + if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0){ + len = (int)wcslen(FindData.cFileName); + if (dir_len + len >= MAX_LEN) + continue; // 長すぎるファイル名は無視する + if (recv_len + dir_len + len >= l_max){ // 領域が足りなくなるなら拡張する + l_max += ALLOC_LEN; + tmp_p = (wchar_t *)realloc(recv_buf, l_max * 2); + if (tmp_p == NULL){ + FindClose(hFind); + printf("realloc, %d\n", l_max * 2); + return 1; + } else { + recv_buf = tmp_p; + } + } + + // リストにコピーする + wcscpy(recv_buf + recv_len, recovery_base); + recv_len += dir_len; + wcscpy(recv_buf + recv_len, FindData.cFileName); + recv_len += len + 1; + recovery_num++; + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + } + } + if (recovery_num > 1) + sort_list(recv_buf, recv_len); // リスト内でファイル名順に並び替える (外部ファイルは並び替えない) + + // 指定された検査対象にリカバリ・ファイルが含まれるかもしれない + if (list2_buf){ + int list2_off = 0; + if (file_ext[0] != 0){ // リカバリ・ファイルの拡張子 + ext_len = (int)wcslen(file_ext); + } else { + ext_len = MAX_LEN; // 拡張子が省略されてる場合は比較しない + } + while (list2_off < list2_len){ + len = (int)wcslen(list2_buf + list2_off); + // ファイル名が重複しないようにする + if (search_file_path(recv_buf, recv_len, list2_buf + list2_off)){ + list2_off += len + 1; + continue; + } + // 拡張子が「.par2」あるいはリカバリ・ファイルと同じ外部ファイルだけ検査する + if (((len > 5) && (_wcsicmp(list2_buf + (list2_off + len - 5), L".par2") == 0)) || + ((len > ext_len) && (_wcsicmp(list2_buf + (list2_off + len - ext_len), file_ext) == 0))){ + if (recv_len + len >= l_max){ // 領域が足りなくなるなら拡張する + l_max += ALLOC_LEN; + tmp_p = (wchar_t *)realloc(recv_buf, l_max * 2); + if (tmp_p == NULL){ + printf("realloc, %d\n", l_max * 2); + return 1; + } else { + recv_buf = tmp_p; + } + } + // リストにコピーする + wcscpy(recv_buf + recv_len, list2_buf + list2_off); + recv_len += len + 1; + recovery_num++; + } + list2_off += len + 1; // 次のファイルへ + } + } + + // 探査時に除外するリストの領域を確保しておく + recv2_len = 0; + recv2_buf = (wchar_t *)malloc(recv_len * 2); + if (recv2_buf == NULL){ + printf("malloc, %d\n", recv_len * 2); + return 1; + } +/*{ +FILE *fp; +fp = fopen("par_list.txt", "wb"); +fwrite(recv_buf, 2, recv_len, fp); +fclose(fp); +}*/ + + return 0; +} + +// Magic sequence と一致するか (match = 0, fail = 1) +static int match_magic(unsigned char *buf) +{ + if ((*buf++) != 'P') + return 1; + if ((*buf++) != 'A') + return 1; + if ((*buf++) != 'R') + return 1; + if ((*buf++) != '2') + return 1; + if ((*buf++) != 0) + return 1; + if ((*buf++) != 'P') + return 1; + if ((*buf++) != 'K') + return 1; + if ((*buf++) != 'T') + return 1; + + return 0; +} + +// Main packet を末尾から遡って探す +int search_main_packet( + unsigned char *buf, // 作業バッファー、File ID が戻る + unsigned char *set_id) // Recovery Set ID が戻る +{ + unsigned char hash[16]; + int num, recv_off, len, off; + unsigned int rv, packet_size, time_last; + __int64 file_size, file_off, end_size; + HANDLE hFile; + + // リカバリ・ファイルの一覧を表示する + printf("PAR File list :\n"); + printf(" Size : Filename\n"); + total_file_size = 0; + recv_off = 0; + while (recv_off < recv_len){ + // ファイルを開くことができるか、サイズを取得できるかを確かめる + hFile = CreateFile(recv_buf + recv_off, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + recv_len = remove_file_path(recv_buf, recv_len, recv_off); // リストから取り除く + recovery_num--; + continue; + } + // ファイルのサイズを取得する + if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){ + CloseHandle(hFile); + recv_len = remove_file_path(recv_buf, recv_len, recv_off); // リストから取り除く + recovery_num--; + continue; + } + CloseHandle(hFile); + total_file_size += file_size; + // リカバリ・ファイルの名前 + if (compare_directory(recovery_file, recv_buf + recv_off) == 0){ // 同じ場所なら + utf16_to_cp(offset_file_name(recv_buf + recv_off), buf, cp_output); // ファイル名だけにする + } else { + path_to_cp(recv_buf + recv_off, buf, cp_output); // パスも表示する + } + printf("%13I64d : \"%s\"\n", file_size, buf); + while (recv_buf[recv_off] != 0) // 次のファイルの位置にずらす + recv_off++; + recv_off++; + } + printf("\nPAR File total size\t: %I64d\n", total_file_size); + printf("PAR File possible count\t: %d\n\n", recovery_num); + + recv_off = 0; // 先頭に戻しておく + prog_last = -1; + end_size = 0; + for (num = 0; num < recovery_num; num++){ + if (cancel_progress() != 0) // キャンセル処理 + return 2; + + // リカバリ・ファイルを開く + if (num == 0){ // まずは指定されたリカバリ・ファイルから探す + hFile = CreateFile(recovery_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + } else { + if (_wcsicmp(recv_buf + recv_off, recovery_file) == 0){ // 最初に検査したリカバリ・ファイルはとばす + while (recv_buf[recv_off] != 0) + recv_off++; + recv_off++; + } + hFile = CreateFile(recv_buf + recv_off, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + while (recv_buf[recv_off] != 0) + recv_off++; + recv_off++; + } + if (hFile == INVALID_HANDLE_VALUE) + continue; // エラーが発生したら次のリカバリ・ファイルを調べる + + // ファイルのサイズを取得する + if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){ + CloseHandle(hFile); + continue; // エラーが発生したら次のリカバリ・ファイルを調べる + } + if (file_size < MIN_PACKET_SIZE){ + CloseHandle(hFile); + continue; // 小さすぎるファイルは検査しない + } + // 読み取りサイズは最大で SEARCH_SIZE + len = SEARCH_SIZE; + if (file_size < SEARCH_SIZE) + len = (unsigned int)file_size; + //printf("num = %d, file_size = %I64d\n", num, file_size); + time_last = GetTickCount(); + + // 末尾から遡って読み込んでいく + file_off = file_size; + while (len > 0){ + file_off -= len; // 読み取り開始位置になる + // SEARCH_SIZE を読み込んでバッファーの先頭に入れる + if (file_read_data(hFile, file_off, buf, len)){ + len = 0; // 読み込み時にエラーが発生したファイルはそれ以上検査しない + break; + } + + // Main packet を探す + off = 0; + while (off < len){ + if (match_magic(buf + off) == 0){ + memcpy(&packet_size, buf + (off + 12), 4); // パケット・サイズの上位4バイトが0以外だとだめ + if (packet_size != 0){ + off += 8; + continue; + } + memcpy(&packet_size, buf + (off + 8), 4); // パケット・サイズを確かめる + if ((packet_size & 3) || (packet_size < MIN_PACKET_SIZE) || (off + packet_size > SEARCH_SIZE * 3)){ + off += 8; + continue; + } + // パケット全体がバッファー内にあれば破損してないかを調べる + data_md5(buf + (off + 32), (packet_size - 32), hash); + if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見 + if (memcmp(buf + (off + 48), "PAR 2.0\0Main\0\0\0\0", 16) == 0){ + // 念のためデータの整合性を検査する + rv = 0; + memcpy(&block_size, buf + (off + 68), 4); // Slice size の上位 4バイトを確かめる + if (block_size != 0) // ブロック・サイズが 4GB以上だとエラー + rv++; + memcpy(&block_size, buf + (off + 64), 4); // Slice size + if (block_size > MAX_BLOCK_SIZE) // 対応するブロック・サイズは 1GB まで + rv++; + if ((block_size & 3) != 0) // ブロック・サイズは 4 の倍数のはず + rv++; + memcpy(&entity_num, buf + (off + 72), 4); // Number of files in the recovery set. + file_num = (packet_size - 76) / 16; + if (file_num <= 0) + rv++; + if (entity_num > file_num) + rv++; + if (rv == 0){ + memcpy(set_id, buf + (off + 32), 16); // Recovery Set ID + memmove(buf, buf + (off + 76), packet_size - 76); // File ID をバッファーの先頭に移す + CloseHandle(hFile); + // Recovery Set の情報を表示する + printf("Recovery Set ID\t\t: "); + print_hash(set_id); + printf("\n"); + printf("Input File Slice size\t: %u\n", block_size); + printf("Input File total count\t: %d\n", file_num); + printf("Recovery Set file count : %d\n", entity_num); + return 0; + } + } + off += packet_size; // パケットの終端まで一気に飛ぶ + } else { + off += 8; + } + } else { + off++; + } + } + + // 簡易検査なら検査領域を制限する(ファイルの後半分だけ) + if (((switch_v & 1) != 0) && (file_off < file_size / 2)){ // 最低でも後半分は検査する + len = 0; + break; + } + + // バッファーの前半 SEARCH_SIZE * 2 を次の読み込みサイズ分だけ後ろにずらす + len = SEARCH_SIZE; + if (file_off < SEARCH_SIZE) + len = (unsigned int)file_off; + if (len > 0) + memmove(buf + len, buf, SEARCH_SIZE * 2); + + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)(((end_size + file_size - file_off) * 1000) / total_file_size))){ + CloseHandle(hFile); + return 2; + } + time_last = GetTickCount(); + } + } + CloseHandle(hFile); + end_size += file_size; + } + if ((prog_last >= 0) && (prog_last != 1000)) + printf("100.0%%\r"); + + return 1; +} + +// ファイル情報のパケットを探す +int search_file_packet( + char *ascii_buf, + unsigned char *buf, // 作業バッファー + wchar_t *par_comment, // Unicode コメントを入れる + unsigned char *set_id, // Recovery Set ID を確かめる + int flag_sanitize, // 0以外 = ファイル名を浄化する + file_ctx_r *files) // 各ソース・ファイルの情報 +{ + char par_client[COMMENT_LEN], ascii_comment[COMMENT_LEN]; + unsigned char hash[16]; + wchar_t uni_buf[MAX_LEN]; + int i, j, k, recv_off, find_num; + int len, off, max, used_CP = CP_UTF8; + unsigned int packet_size, time_last; + __int64 file_size, file_off, file_next, end_size; + HANDLE hFile; + + par_client[0] = 0; + par_comment[0] = 0; + ascii_comment[0] = 0; + + list_len = 1; // 先頭に不明用の null 文字を置く + list_max = ALLOC_LEN; + list_buf = (wchar_t *)malloc(list_max * 2); + if (list_buf == NULL){ + printf("malloc, %d\n", list_max * 2); + return 1; + } + list_buf[0] = 0; + + find_num = 0; + recv_off = 0; + end_size = 0; + while (recv_off < recv_len){ + if (cancel_progress() != 0) // キャンセル処理 + return 2; + + //utf16_to_cp(recv_buf + recv_off, ascii_buf, cp_output); + //printf("verifying %s\n", ascii_buf); + // リカバリ・ファイルを開く + hFile = CreateFile(recv_buf + recv_off, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + while (recv_buf[recv_off] != 0) // 次のファイルの位置にずらす + recv_off++; + recv_off++; + if (hFile == INVALID_HANDLE_VALUE) + continue; // エラーが発生したら次のリカバリ・ファイルを調べる + // ファイルのサイズを取得する + if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){ + CloseHandle(hFile); + continue; // エラーが発生したら次のリカバリ・ファイルを調べる + } + if (file_size < MIN_PACKET_SIZE){ + CloseHandle(hFile); + continue; // 小さすぎるファイルは検査しない + } + file_off = 0; + time_last = GetTickCount(); + + // 最初はファイルの先頭 SEARCH_SIZE * 3 を読み込む + off = 0; + len = SEARCH_SIZE * 3; + if (file_size < SEARCH_SIZE * 3) + len = (unsigned int)file_size; + // 最大 SEARCH_SIZE * 3 を読み込んでバッファーの先頭に入れる + if (file_read_data(hFile, 0, buf, len)){ + CloseHandle(hFile); + continue; // エラーが発生したら次のリカバリ・ファイルを調べる + } + file_next = len; // 次の読み込み開始位置 + max = len; + if (len > SEARCH_SIZE) + max = SEARCH_SIZE; + + // パケットを探す + while (len > 0){ + while (off < max){ + if (match_magic(buf + off) == 0){ + memcpy(&packet_size, buf + (off + 12), 4); // パケット・サイズの上位4バイトが0以外だとだめ + if (packet_size != 0){ + off += 8; + continue; + } + memcpy(&packet_size, buf + (off + 8), 4); // パケット・サイズを確かめる + if ((packet_size & 3) || (packet_size < MIN_PACKET_SIZE) || (file_off + (__int64)off + (__int64)packet_size > file_size)){ + off += 8; + continue; + } + if (off + packet_size > SEARCH_SIZE * 3){ // 大きすぎてバッファー内に収まってないなら + if (packet_size > 64 + 4 + block_size){ // Recovery Slice packet よりも大きいパケットは無いはず + off += 8; + continue; + } + if ((switch_v & 1) == 0){ // 簡易検査でなければ + // パケットが破損してないかを調べる + if (file_md5(hFile, file_off + off + 32, packet_size - 32, hash)){ + off += 8; // 計算できなかったら + } else { + if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見 + // バッファー内のオフセットとパケット・サイズ分ずらす + if (file_next < file_size) + file_next = file_off + off + packet_size; + off = max; // 一気に末尾までいく + } else { + off += 8; // 破損パケットなら + } + } + } else { // 簡易検査では巨大パケットが完全かは調べない + // バッファー内のオフセットとパケット・サイズ分ずらす + if (file_next < file_size) + file_next = file_off + off + packet_size; + off = max; // 一気に末尾までいく + } + continue; + } + // パケットが破損してないかを調べる + data_md5(buf + (off + 32), (packet_size - 32), hash); + if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見 + if (memcmp(buf + (off + 32), set_id, 16) != 0){ // Recovery Set ID が同じパケットだけ + off += packet_size; + continue; + } +/* +memset(ascii_buf, 0, 9); +memcpy(ascii_buf, buf + (off + 56), 8); +printf(" packet : %s, size = %u, file_off = %I64d, off = %d\n", ascii_buf, packet_size, file_off, off); +*/ + if ((memcmp(buf + (off + 48), "PAR 2.0\0FileDesc", 16) == 0) && (packet_size - 120 < MAX_LEN * 3)){ + // File Description packet + memcpy(ascii_buf, buf + (off + 120), packet_size - 120); + ascii_buf[packet_size - 120] = 0; // 末尾を null 文字にしておく + j = -1; // File ID が重複してないか確認する + for (i = 0; i < file_num; i++){ + if (memcmp(buf + (off + 64), files[i].id, 16) == 0){ // File ID でどのファイルか探す + if (files[i].name < 0){ // 新しく発見したのなら + break; + } else { // 同じ情報が既に在った場合 + j = i; + } + } + } + if (i < file_num){ + memcpy(files[i].hash, buf + (off + 80), 32); + memcpy(&(files[i].size), buf + (off + 112), 8); + if ((j >= 0) && (i != j)){ // File ID が同じファイルが他に存在する + // ファイル内容が異なるのに同じ File ID になると区別できないので駄目! + // 逆に、ファイル内容 (ハッシュ値とサイズ) が同じなら問題ない? + if ((files[i].size != files[j].size) || + (memcmp(files[i].hash, files[j].hash, 32) != 0)){ + printf("same File ID, %d and %d\n", j, i); + return 1; + } + } + // パケット内に記録されてるファイル名をユニコードに変換する + if (used_CP != CP_UTF8){ // QuickPar などファイル名が UTF-8 以外なら + // 変換できるか調べる + j = cp_to_utf16(ascii_buf, uni_buf, used_CP); + if (j == ERROR_NO_UNICODE_TRANSLATION){ // CP1252 では変換エラーは発生しない + j = cp_to_utf16(ascii_buf, uni_buf, 1252); + if (j == 0){ + k = convert_filename(1252, used_CP, files); // これまでのファイル名を変換しなおす + if (k != 0){ + printf("convert_filename\n"); + return 1; + } + } + used_CP = 1252; // これ以降のファイル名は Latin-1 CP1252 とみなす + } + } else if (check_utf8(ascii_buf) != 0){ // UTF-8 以外の文字をみつけたら + used_CP = CP_ACP; // これ以降のファイル名は全て UTF-8 以外とみなす + // ACP で変換できるか調べる + j = cp_to_utf16(ascii_buf, uni_buf, used_CP); + if (j == ERROR_NO_UNICODE_TRANSLATION){ // CP1252 では変換エラーは発生しない + j = cp_to_utf16(ascii_buf, uni_buf, 1252); + used_CP = 1252; // これ以降のファイル名は Latin-1 CP1252 とみなす + } + if (j == 0){ + k = convert_filename(used_CP, CP_UTF8, files); // これまでのファイル名を変換しなおす + if (k == 1){ // CP1252 では変換エラーは発生しない + j = cp_to_utf16(ascii_buf, uni_buf, 1252); + k = convert_filename(1252, CP_UTF8, files); + used_CP = 1252; // これ以降のファイル名は Latin-1 CP1252 とみなす + } + if (k != 0){ + printf("convert_filename\n"); + return 1; + } + } + } else { + utf8_to_utf16(ascii_buf, uni_buf); + j = 0; + } + if (j == 0){ + files[i].name = list_len; + if (add_file_path(uni_buf)){ + printf("add_file_path\n"); + return 1; + } + } else { + files[i].name = 0; // パケットは存在するけどファイル名の変換に失敗した + } + find_num++; // 完全な File Description packet の数 + } + } else if ((par_client[0] == 0) && (memcmp(buf + (off + 48), "PAR 2.0\0Creator\0", 16) == 0)){ + // Creater packet + i = packet_size - 64; // Creater のバイト数 + if (i >= COMMENT_LEN) + i = COMMENT_LEN - 1; + memcpy(par_client, buf + (off + 64), i); + par_client[i] = 0; // 末尾を null 文字にする + } else if ((par_comment[0] == 0) && (ascii_comment[0] == 0) && (memcmp(buf + (off + 48), "PAR 2.0\0CommASCI", 16) == 0)){ + // ASCII Comment packet + i = packet_size - 64; // コメントのバイト数 + if (i >= COMMENT_LEN) + i = COMMENT_LEN - 1; + memcpy(ascii_comment, buf + (off + 64), i); + ascii_comment[i] = 0; // 末尾を null 文字にする + } else if ((par_comment[0] == 0) && (memcmp(buf + (off + 48), "PAR 2.0\0CommUni\0", 16) == 0)){ + // Unicode Comment packet + i = packet_size - 80; // コメントのバイト数 + if (i >= COMMENT_LEN * 2) + i = COMMENT_LEN * 2 - 2; + memcpy(par_comment, buf + (off + 80), i); + par_comment[i / 2] = 0; // 末尾を null 文字にする + } else if ((memcmp(buf + (off + 48), "PAR 2.0\0UniFileN", 16) == 0) && (packet_size - 80 < MAX_LEN * 2)){ + // Unicode Filename packet + memset(uni_buf, 0, sizeof(uni_buf)); + memcpy(uni_buf, buf + (off + 80), packet_size - 80); + for (i = 0; i < file_num; i++){ + if (memcmp(buf + (off + 64), files[i].id, 16) == 0) // File ID が一致すれば + break; + } + if (i < file_num){ // File Description packet を先に読み込んでる状態でしか認識しない + if (files[i].name == 0){ // パケットは存在するけどファイル名の変換に失敗してる場合 + // 新しいファイル名 + files[i].name = list_len; + if (add_file_path(uni_buf)){ + printf("add_file_path\n"); + return 1; + } + } else if ((files[i].name > 0) && (wcscmp(uni_buf, list_buf + files[i].name) != 0)){ + // パケットが存在してファイル名も取得してるけどユニコード版とは異なる場合 + k = (int)wcslen(list_buf + files[i].name); + list_len = remove_file_path(list_buf, list_len, files[i].name); + for (j = 0; j < file_num; j++){ + if (files[j].name > files[i].name) + files[j].name -= (k + 1); // リスト上で後ろのやつをずらしていく + } + // 新しいファイル名 + files[i].name = list_len; + if (add_file_path(uni_buf)){ + printf("add_file_path\n"); + return 1; + } + } + } + } + off += packet_size; // パケットの終端まで一気に飛ぶ + } else { + off += 8; + } + } else { + off++; + } + } + // 全てのファイル情報を取得してしまえば抜ける + if ((find_num >= file_num) && (par_client[0] != 0)) + break; + + if (file_next < file_size){ // ファイル・データがまだ残ってれば + if (file_next > file_off + SEARCH_SIZE * 3){ // 次の読み込み位置が前回よりも SEARCH_SIZE * 3 以上離れてるなら + file_off = file_next; + // 読み取りサイズは最大で SEARCH_SIZE * 3 + off = 0; + len = SEARCH_SIZE * 3; + if (file_size < file_next + SEARCH_SIZE * 3) + len = (unsigned int)(file_size - file_next); + // 最大 SEARCH_SIZE * 3 を読み込んでバッファーの先頭に入れる + if (file_read_data(hFile, file_next, buf, len)){ + off = len; // 読み込み時にエラーが発生した部分は検査しない + file_next = file_size; // 次は読み込まず、このファイルの検査を終える + } + file_next += len; // 次の読み込み開始位置 + max = len; + if (len > SEARCH_SIZE) + max = SEARCH_SIZE; + } else { // 次の SEARCH_SIZE を読み込む + // バッファーの内容を前にずらす + memcpy(buf, buf + SEARCH_SIZE, SEARCH_SIZE); + memcpy(buf + SEARCH_SIZE, buf + SEARCH_SIZE * 2, SEARCH_SIZE); + file_off += SEARCH_SIZE; + // 読み取りサイズは最大で SEARCH_SIZE + off -= max; + len = SEARCH_SIZE; + if (file_size < file_next + SEARCH_SIZE) + len = (unsigned int)(file_size - file_next); + // 最大 SEARCH_SIZE を読み込んでバッファーの末尾に入れる + if (file_read_data(hFile, file_next, buf + SEARCH_SIZE * 2, len)){ + memset(buf + SEARCH_SIZE * 2, 0, len); // 読み込み時にエラーが発生した部分は 0 にしておく + file_next = file_size; // 次は読み込まず、このファイルの検査を終える + } + file_next += len; // 次の読み込み開始位置 + max = SEARCH_SIZE; + len += SEARCH_SIZE * 2; // 未処理データのサイズに残ってる SEARCH_SIZE * 2 を足す + } + } else { // バッファー内の残りデータを処理する + off -= max; + len -= max; + if (len > 0){ + if (len > SEARCH_SIZE){ + max = SEARCH_SIZE; + memcpy(buf, buf + SEARCH_SIZE, SEARCH_SIZE); + memcpy(buf + SEARCH_SIZE, buf + SEARCH_SIZE * 2, len - SEARCH_SIZE); + } else { + max = len; + memcpy(buf, buf + SEARCH_SIZE, len); // バッファーの内容を前にずらす + } + file_off += SEARCH_SIZE; + } + } + + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)(((end_size + file_off) * 1000) / total_file_size))){ + CloseHandle(hFile); + return 2; + } + time_last = GetTickCount(); + } + } + CloseHandle(hFile); + end_size += file_size; + + // 全てのファイル情報を取得してしまえば抜ける + if ((find_num >= file_num) && (par_client[0] != 0)) + break; + } + + // QuickPar と phpar2 はファイル名を UTF-8 で記録しないことがわかってる + if ((used_CP == CP_UTF8) && (par_client[0] != 0) && (non_utf8_client(par_client) != 0)){ + k = convert_filename(CP_ACP, CP_UTF8, files); // これまでのファイル名を変換しなおす + if (k == 1){ + used_CP = 1252; // Latin-1 CP1252 で変換を試みる + k = convert_filename(1252, CP_UTF8, files); + } + if (k != 0){ + printf("convert_filename\n"); + return 1; + } + } + + // 作成したクライアントとコメントを表示する + if (par_client[0] != 0){ + for (i = 0; i < COMMENT_LEN; i++){ // 表示する前に sanitalize する + if (par_client[i] == 0){ + break; + } else if ((par_client[i] <= 31) || (par_client[i] >= 127)){ // 制御文字を消す、非 ASCII 文字も + par_client[i] = ' '; + } + } + printf("Creator : %s\n", par_client); + } + if ((par_comment[0] == 0) && (ascii_comment[0] != 0)){ // ユニコードのコメントを優先する + if (!MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, ascii_comment, -1, par_comment, COMMENT_LEN)) + par_comment[0] = 0; + } + if (par_comment[0] != 0){ + for (i = 0; i < COMMENT_LEN; i++){ // 表示する前に sanitalize する + if (par_comment[i] == 0){ + break; + } else if ((par_comment[i] <= 31) || (par_comment[i] == 127)){ // 制御文字を消す + par_comment[i] = ' '; + } + } + if (!utf16_to_cp(par_comment, ascii_buf, cp_output)) + printf("Comment : %s\n", ascii_buf); + } + write_ini_file2(par_client, par_comment); + + // リスト内のファイル名を検査して、問題があれば浄化する + // ついでにディレクトリ記号を「\」に統一する + if (flag_sanitize != 0){ + max = 0; + for (i = 0; i < file_num; i++){ + if (files[i].name <= 0) + continue; // ファイル名が取得できなかったファイルはとばす + wcscpy(uni_buf, list_buf + files[i].name); + off = sanitize_filename(uni_buf, files, i); + if (off != 0){ + if ((off == 1) || (off == 9)){ // ディレクトリ記号を含んでる + wcscpy(list_buf + files[i].name, uni_buf); + off ^= 1; + } + if (off != 0){ + if (max == 0){ + max = 1; + printf("\nWarning about filenames :\n"); + } + utf16_to_cp(uni_buf, ascii_buf, cp_output); + if (off == 16){ + max++; + printf("file%d: \"%s\" is invalid\n", i, ascii_buf); + } else if (off == 8){ + printf("file%d: \"%s\" is invalid\n", i, ascii_buf); + } else { + printf("file%d: \"%s\" was sanitized\n", i, ascii_buf); + if (off & 4){ // 文字数が変わった + k = (int)wcslen(list_buf + files[i].name); + list_len = remove_file_path(list_buf, list_len, files[i].name); + for (j = 0; j < file_num; j++){ + if (files[j].name > files[i].name) + files[j].name -= (k + 1); // リスト上で後ろのやつをずらしていく + } + // 新しいファイル名 + files[i].name = list_len; + if (add_file_path(uni_buf)){ + printf("add_file_path\n"); + return 1; + } + } else { // 文字数が同じなら元の場所にコピーする + wcscpy(list_buf + files[i].name, uni_buf); + } + } + } + } + } + if (max > 1) + return 1; + } + + return 0; +} + +// 修復用のパケットを探す +// 0~= 不完全なリカバリ・ファイルの数を返す、-2=Cansel +int search_recovery_packet( + char *ascii_buf, + unsigned char *buf, // 作業バッファー + wchar_t *file_path, + unsigned char *set_id, // Recovery Set ID を確かめる + HANDLE *rcv_hFile, // 各リカバリ・ファイルのハンドル (verify なら NULL) + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + parity_ctx_r *p_blk) // 各パリティ・ブロックの情報 +{ + unsigned char hash[16]; + int i, j, recv_off, num, packet_count, bad_flag, file_block; + int find_num, find_new, recovery_lost; + int len, off, max; + unsigned int packet_size, time_last, meta_data[7]; + __int64 file_size, file_off, file_next; + HANDLE hFile; + + printf("\nLoading PAR File :\n"); + printf(" Packet Slice Status : Filename\n"); + fflush(stdout); + count_last = 0; + recovery_lost = 0; // 不完全なリカバリ・ファイルの数 + first_num = 0; // 初めて見つけたパリティ・ブロックの数 + num = 0; + recv_off = 0; + while (recv_off < recv_len){ + if (cancel_progress() != 0) // キャンセル処理 + return -2; + + //utf16_to_cp(recv_buf + recv_off, ascii_buf, cp_output); + //printf("verifying %s\n", ascii_buf); + // リカバリ・ファイルを開く + hFile = CreateFile(recv_buf + recv_off, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + i = -1; + } else { + // 検査するリカバリ・ファイルが同じであれば、再検査する必要は無い + i = check_ini_recovery(hFile, meta_data); + } + if (i < 0){ // エラーが発生したら次のリカバリ・ファイルを調べる + while (recv_buf[recv_off] != 0) // 次のファイルの位置にずらす + recv_off++; + recv_off++; + continue; + } + if (i == 0){ + memcpy(&file_size, meta_data, 8); + find_num = 0; + find_new = 0; + file_off = 0; + prog_last = -1; + time_last = GetTickCount(); + + // 最初はファイルの先頭 SEARCH_SIZE * 3 を読み込む + bad_flag = 0; + off = 0; + len = SEARCH_SIZE * 3; + if (file_size < SEARCH_SIZE * 3) + len = (unsigned int)file_size; + if (file_size < MIN_PACKET_SIZE){ // 小さすぎるファイルは検査しない + bad_flag |= 1; + len = 0; + } + // 最大 SEARCH_SIZE * 3 を読み込んでバッファーの先頭に入れる + if (file_read_data(hFile, 0, buf, len)){ + off = len; // 読み込み時にエラーが発生した部分は検査しない + bad_flag |= 128; + file_next = file_size; // 次は読み込まず、このファイルの検査を終える + } else { + file_next = len; // 次の読み込み開始位置 + } + max = len; + if (len > SEARCH_SIZE) + max = SEARCH_SIZE; + //printf("file_size = %I64d, max = %d, off = %d, len = %d\n", file_size, max, off, len); + + // パケットを探す + packet_count = 0; + while (len > 0){ + while (off < max){ + if (match_magic(buf + off) == 0){ + memcpy(&packet_size, buf + (off + 12), 4); // パケット・サイズの上位4バイトが0以外だとだめ + if (packet_size != 0){ + bad_flag |= 1; + off += 8; + continue; + } + memcpy(&packet_size, buf + (off + 8), 4); // パケット・サイズを確かめる + if ((packet_size & 3) || (packet_size < MIN_PACKET_SIZE) || (file_off + (__int64)off + (__int64)packet_size > file_size)){ + bad_flag |= 1; + off += 8; + continue; + } + if (off + packet_size > SEARCH_SIZE * 3){ // 大きすぎてバッファー内に収まってないなら + if (packet_size > 64 + 4 + block_size){ // Recovery Slice packet よりも大きいパケットは無いはず + off += 8; + continue; + } + // パケットが破損してないかを調べる + if (file_md5(hFile, file_off + off + 32, packet_size - 32, hash)){ + off += 8; // 計算できなかったら + } else { + if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見 + if (memcmp(buf + (off + 32), set_id, 16) == 0){ // Recovery Set ID が同じなら + // 少なくともパケットの先頭 SEARCH_SIZE * 2 まではバッファー内に存在する + // バッファーに収まらないパケットは Recovery Slice packet だけのはず + if (memcmp(buf + (off + 48), "PAR 2.0\0RecvSlic", 16) == 0){ + // Recovery Slice packet + memcpy(&j, buf + (off + 64), 4); // パリティ・ブロックの番号 + j &= 0xFFFF; // 番号は 16-bit (65536以上は 0~65535 の繰り返し) + if (j == 65535) // (0 と 65535 は同じ) + j = 0; + if (j < parity_num){ + if (p_blk[j].exist == 0){ + find_new++; // リカバリ・ファイル内の新しいパリティ・ブロックの数 + first_num++; + p_blk[j].exist = 1; + p_blk[j].file = num; + p_blk[j].off = file_off + off + 68; + } + write_ini_recovery(j, file_off + off + 68); + find_num++; // リカバリ・ファイル内のパリティ・ブロックの数 + } + } + packet_count++; + } else { + bad_flag |= 2; // 別の Set ID が混じってる + } + // バッファー内のオフセットとパケット・サイズ分ずらす + if (file_next < file_size) + file_next = file_off + off + packet_size; + off = max; // 一気に末尾までいく + } else { + off += 8; // 破損パケットなら + } + } + continue; + } + // パケットが破損してないかを調べる + data_md5(buf + (off + 32), (packet_size - 32), hash); + if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見 + if (memcmp(buf + (off + 32), set_id, 16) != 0){ // Recovery Set ID が同じパケットだけ + bad_flag |= 2; // 別の Set ID が混じってる + off += packet_size; + continue; + } +/* +memset(ascii_buf, 0, 9); +memcpy(ascii_buf, buf + (off + 56), 8); +printf(" packet : %s, size = %u, file_off = %I64d, off = %d\n", ascii_buf, packet_size, file_off, off); +*/ + if (memcmp(buf + (off + 48), "PAR 2.0\0IFSC\0\0\0\0", 16) == 0){ + // Input File Slice Checksum packet + for (j = 0; j < entity_num; j++){ + if (memcmp(buf + (off + 64), files[j].id, 16) == 0){ // File ID が一致すれば + if (files[j].state & 0x80) // チェックサムを新しく発見したのなら + break; + } + } + file_block = files[j].b_num; // ソース・ブロックの数 + if ((j < entity_num) && ((int)((packet_size - 80) / 20) == file_block)){ + files[j].state = 0; // そのファイルのチェックサムを見つけた + for (i = 0; i < file_block; i++){ + memcpy(s_blk[files[j].b_off + i].hash, buf + (off + 80 + (20 * i)), 20); + memcpy(&(s_blk[files[j].b_off + i].crc), buf + (off + 80 + (20 * i) + 16), 4); + s_blk[files[j].b_off + i].crc ^= window_mask; // CRC の初期値と最終処理の 0xFFFFFFFF を取り除く + } + } + } else if (memcmp(buf + (off + 48), "PAR 2.0\0RecvSlic", 16) == 0){ + // Recovery Slice packet + memcpy(&j, buf + (off + 64), 4); // パリティ・ブロックの番号 + j &= 0xFFFF; // 番号は 16-bit (65536以上は 0~65535 の繰り返し) + if (j == 65535) // (0 と 65535 は同じ) + j = 0; + if ((packet_size == 64 + 4 + block_size) && (j < parity_num)){ + if (p_blk[j].exist == 0){ + find_new++; // リカバリ・ファイル内の新しいパリティ・ブロックの数 + first_num++; + p_blk[j].exist = 1; + p_blk[j].file = num; + p_blk[j].off = file_off + off + 68; + } + write_ini_recovery(j, file_off + off + 68); + find_num++; // リカバリ・ファイル内のパリティ・ブロックの数 + } + } + packet_count++; + off += packet_size; // パケットの終端まで一気に飛ぶ + } else { + bad_flag |= 1; + off += 8; + } + } else { + bad_flag |= 1; + off++; + } + } + + if (file_next < file_size){ // ファイル・データがまだ残ってれば + if (file_next > file_off + SEARCH_SIZE * 3){ // 次の読み込み位置が前回よりも SEARCH_SIZE * 3 以上離れてるなら + file_off = file_next; + // 読み取りサイズは最大で SEARCH_SIZE * 3 + off = 0; + len = SEARCH_SIZE * 3; + if (file_size < file_next + SEARCH_SIZE * 3) + len = (unsigned int)(file_size - file_next); + // 最大 SEARCH_SIZE * 3 を読み込んでバッファーの先頭に入れる + if (file_read_data(hFile, file_next, buf, len)){ + off = len; // 読み込み時にエラーが発生した部分は検査しない + bad_flag |= 128; + file_next = file_size; // 次は読み込まず、このファイルの検査を終える + } + file_next += len; // 次の読み込み開始位置 + max = len; + if (len > SEARCH_SIZE) + max = SEARCH_SIZE; + } else { // 次の SEARCH_SIZE を読み込む + // バッファーの内容を前にずらす + memcpy(buf, buf + SEARCH_SIZE, SEARCH_SIZE); + memcpy(buf + SEARCH_SIZE, buf + SEARCH_SIZE * 2, SEARCH_SIZE); + file_off += SEARCH_SIZE; + // 読み取りサイズは最大で SEARCH_SIZE + off -= max; + len = SEARCH_SIZE; + if (file_size < file_next + SEARCH_SIZE) + len = (unsigned int)(file_size - file_next); + // 最大 SEARCH_SIZE を読み込んでバッファーの末尾 SEARCH_SIZE に入れる + if (file_read_data(hFile, file_next, buf + SEARCH_SIZE * 2, len)){ + memset(buf + SEARCH_SIZE * 2, 0, len); // 読み込み時にエラーが発生した部分は 0 にしておく + bad_flag |= 128; + file_next = file_size; // 次は読み込まず、このファイルの検査を終える + } + file_next += len; // 次の読み込み開始位置 + max = SEARCH_SIZE; + len += SEARCH_SIZE * 2; // 未処理データのサイズに残ってる SEARCH_SIZE * 2 を足す + } + } else { // バッファー内の残りデータを処理する + //printf("max = %d, off = %d, len = %d\n", max, off, len); + off -= max; + len -= max; + if (len > 0){ + if (len > SEARCH_SIZE){ + max = SEARCH_SIZE; + memcpy(buf, buf + SEARCH_SIZE, SEARCH_SIZE); + memcpy(buf + SEARCH_SIZE, buf + SEARCH_SIZE * 2, len - SEARCH_SIZE); + } else { + max = len; + memcpy(buf, buf + SEARCH_SIZE, len); // バッファーの内容を前にずらす + } + file_off += SEARCH_SIZE; + } + } + + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress_file((int)((file_off * 1000) / file_size), first_num, recv_buf + recv_off)){ + CloseHandle(hFile); + write_ini_recovery2(-1, 0, 0, meta_data); + return -2; + } + time_last = GetTickCount(); + } + } + write_ini_recovery2(packet_count, find_num, bad_flag, meta_data); + } else { // 検査済みなら記録を読み込む + find_new = read_ini_recovery(num, &packet_count, &find_num, &bad_flag, p_blk); + if (find_new < 0){ + bad_flag = 1; + packet_count = 0; + find_num = 0; + } else { + first_num += find_new; // 新たに発見したパリティ・ブロックの数を合計する + } + } + print_progress_file(-1, first_num, NULL); // 重複を除外した合計ブロック数を表示する + if ((find_new > 0) && (rcv_hFile != NULL)){ + rcv_hFile[num] = hFile; // 後から読み込めるように開いたままにする + } else { + CloseHandle(hFile); + } + + // リカバリ・ファイルの名前 + if (compare_directory(recovery_file, recv_buf + recv_off) == 0){ // 同じ場所なら + utf16_to_cp(offset_file_name(recv_buf + recv_off), ascii_buf, cp_output); // ファイル名だけにする + } else { + path_to_cp(recv_buf + recv_off, ascii_buf, cp_output); // パスも表示する + } + if (bad_flag == 0){ + // 良好 (パケット単位なので完全に元と同じかは判別できない) + printf("%7d %5d Good : \"%s\"\n", packet_count, find_num, ascii_buf); + // 削除する時用に記録しておく + len = (int)wcslen(recv_buf + recv_off); + wcscpy(recv2_buf + recv2_len, recv_buf + recv_off); + recv2_len += len + 1; + // 次のファイルの位置にずらす + recv_off += len + 1; + } else if (packet_count == 0){ + // パケットが全く見つからなかった (リカバリ・ファイルではない) + // 別の Set ID のリカバリ・ファイルも含む + printf(" 0 0 Useless : \"%s\"\n", ascii_buf); + recovery_lost++; + if (bad_flag == 2){ // 別の Set ID のリカバリ・ファイルで良好なら + // 次のファイルの位置にずらす + while (recv_buf[recv_off] != 0) + recv_off++; + recv_off++; + } else { // リカバリ・ファイルでなければリストから取り除く (ファイル数はそのまま) + recv_len = remove_file_path(recv_buf, recv_len, recv_off); + } + } else { + printf("%7d %5d Damaged : \"%s\"\n", packet_count, find_num, ascii_buf); + recovery_lost++; + // 削除する時用に記録しておく + len = (int)wcslen(recv_buf + recv_off); + wcscpy(recv2_buf + recv2_len, recv_buf + recv_off); + recv2_len += len + 1; + // 破損したリカバリ・ファイルはリストから取り除く (ファイル数はそのまま) + recv_len = remove_file_path(recv_buf, recv_len, recv_off); + } + fflush(stdout); + num++; // 次のファイルへ + } +/*{ +FILE *fp; +fp = fopen("par_list.txt", "wb"); +fwrite(recv_buf, 2, recv_len, fp); +fclose(fp); +fp = fopen("par_list2.txt", "wb"); +fwrite(recv2_buf, 2, recv2_len, fp); +fclose(fp); +}*/ + json_file_list(files); // JSONファイルに記録する + + return recovery_lost; +} + diff --git a/source/par2j/search.h b/source/par2j/search.h new file mode 100644 index 0000000..ddb34cc --- /dev/null +++ b/source/par2j/search.h @@ -0,0 +1,42 @@ +#ifndef _SEARCH_H_ +#define _SEARCH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +// リカバリ・ファイルを検索してファイル・リストに追加する +int search_recovery_files(void); + +// Main packet を末尾から遡って探す +int search_main_packet( + unsigned char *buf, // 作業バッファー、File ID が戻る + unsigned char *set_id); // Recovery Set ID が戻る + +// ファイル情報のパケットを探す +int search_file_packet( + char *ascii_buf, + unsigned char *buf, // 作業バッファー + wchar_t *par_commentU, // Unicode コメントを入れる + unsigned char *set_id, // Recovery Set ID を確かめる + int flag_sanitize, // 0以外 = ファイル名を浄化する + file_ctx_r *files); // 各ソース・ファイルの情報 + +// 修復用のパケットを探す +int search_recovery_packet( + char *ascii_buf, + unsigned char *buf, // 作業バッファー + wchar_t *uni_buf, + unsigned char *set_id, // Recovery Set ID を確かめる + HANDLE *rcv_hFile, // 各リカバリ・ファイルのハンドル (verify なら NULL) + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + parity_ctx_r *p_blk); // 各パリティ・ブロックの情報 + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par2j/source.cl b/source/par2j/source.cl new file mode 100644 index 0000000..e057486 --- /dev/null +++ b/source/par2j/source.cl @@ -0,0 +1,187 @@ +void calc_table(__local uint *mtab, int id, int factor) +{ + int i, sum = 0; + + for (i = 0; i < 8; i++){ + sum = (id & (1 << i)) ? (sum ^ factor) : sum; + factor = (factor & 0x8000) ? ((factor << 1) ^ 0x1100B) : (factor << 1); + } + mtab[id] = sum; + + sum = (sum << 4) ^ (((sum << 16) >> 31) & 0x88058) ^ (((sum << 17) >> 31) & 0x4402C) ^ (((sum << 18) >> 31) & 0x22016) ^ (((sum << 19) >> 31) & 0x1100B); + sum = (sum << 4) ^ (((sum << 16) >> 31) & 0x88058) ^ (((sum << 17) >> 31) & 0x4402C) ^ (((sum << 18) >> 31) & 0x22016) ^ (((sum << 19) >> 31) & 0x1100B); + + mtab[id + 256] = sum; +} + +__kernel void method0( + __global uint *src, + __global uint *dst, + __global ushort *factors, + int blk_num) +{ + __local uint mtab[512]; + int i, blk; + uint v, sum; + const int work_id = get_global_id(0); + const int work_size = get_global_size(0); + const int table_id = get_local_id(0); + + for (i = work_id; i < BLK_SIZE; i += work_size) + dst[i] = 0; + + for (blk = 0; blk < blk_num; blk++){ + calc_table(mtab, table_id, factors[blk]); + barrier(CLK_LOCAL_MEM_FENCE); + + for (i = work_id; i < BLK_SIZE; i += work_size){ + v = src[i]; + sum = mtab[(uchar)(v >> 16)] ^ mtab[256 + (v >> 24)]; + sum <<= 16; + sum ^= mtab[(uchar)v] ^ mtab[256 + (uchar)(v >> 8)]; + dst[i] ^= sum; + } + src += BLK_SIZE; + barrier(CLK_LOCAL_MEM_FENCE); + } +} + +__kernel void method2( + __global uint *src, + __global uint *dst, + __global ushort *factors, + int blk_num) +{ + __local uint mtab[512]; + int i, blk, pos; + uint lo, hi, sum1, sum2; + const int work_id = get_global_id(0) * 2; + const int work_size = get_global_size(0) * 2; + const int table_id = get_local_id(0); + + for (i = work_id; i < BLK_SIZE; i += work_size){ + dst[i ] = 0; + dst[i + 1] = 0; + } + + for (blk = 0; blk < blk_num; blk++){ + calc_table(mtab, table_id, factors[blk]); + barrier(CLK_LOCAL_MEM_FENCE); + + for (i = work_id; i < BLK_SIZE; i += work_size){ + pos = (i & ~7) + ((i & 7) >> 1); + lo = src[pos ]; + hi = src[pos + 4]; + sum1 = mtab[(uchar)(lo >> 16)] ^ mtab[256 + (uchar)(hi >> 16)]; + sum2 = mtab[lo >> 24] ^ mtab[256 + (hi >> 24)]; + sum1 <<= 16; + sum2 <<= 16; + sum1 ^= mtab[(uchar)lo] ^ mtab[256 + (uchar)hi]; + sum2 ^= mtab[(uchar)(lo >> 8)] ^ mtab[256 + (uchar)(hi >> 8)]; + dst[pos ] ^= (sum1 & 0x00FF00FF) | ((sum2 & 0x00FF00FF) << 8); + dst[pos + 4] ^= ((sum1 & 0xFF00FF00) >> 8) | (sum2 & 0xFF00FF00); + } + src += BLK_SIZE; + barrier(CLK_LOCAL_MEM_FENCE); + } +} + +__kernel void method3( + __global uint *src, + __global uint *dst, + __global ushort *factors, + int blk_num) +{ + __global uint *blk_src; + __local uint mtab[512]; + int i, blk, chk_size, remain, pos; + uint lo, hi, sum1, sum2; + const int work_id = get_global_id(0) * 2; + const int work_size = get_global_size(0) * 2; + const int table_id = get_local_id(0); + + remain = BLK_SIZE; + chk_size = CHK_SIZE; + while (remain > 0){ + if (chk_size > remain) + chk_size = remain; + + for (i = work_id; i < chk_size; i += work_size){ + dst[i ] = 0; + dst[i + 1] = 0; + } + + blk_src = src; + for (blk = 0; blk < blk_num; blk++){ + calc_table(mtab, table_id, factors[blk]); + barrier(CLK_LOCAL_MEM_FENCE); + + for (i = work_id; i < chk_size; i += work_size){ + pos = (i & ~7) + ((i & 7) >> 1); + lo = blk_src[pos ]; + hi = blk_src[pos + 4]; + sum1 = mtab[(uchar)(lo >> 16)] ^ mtab[256 + (uchar)(hi >> 16)]; + sum2 = mtab[lo >> 24] ^ mtab[256 + (hi >> 24)]; + sum1 <<= 16; + sum2 <<= 16; + sum1 ^= mtab[(uchar)lo] ^ mtab[256 + (uchar)hi]; + sum2 ^= mtab[(uchar)(lo >> 8)] ^ mtab[256 + (uchar)(hi >> 8)]; + dst[pos ] ^= (sum1 & 0x00FF00FF) | ((sum2 & 0x00FF00FF) << 8); + dst[pos + 4] ^= ((sum1 & 0xFF00FF00) >> 8) | (sum2 & 0xFF00FF00); + } + blk_src += BLK_SIZE; + barrier(CLK_LOCAL_MEM_FENCE); + } + + src += CHK_SIZE; + dst += CHK_SIZE; + remain -= CHK_SIZE; + } +} + +__kernel void method4( + __global uint *src, + __global uint *dst, + __global ushort *factors, + int blk_num) +{ + __local int table[16]; + __local uint cache[256]; + int i, j, blk, pos, sht, mask; + uint sum; + const int work_id = get_global_id(0); + const int work_size = get_global_size(0); + + for (i = work_id; i < BLK_SIZE; i += work_size) + dst[i] = 0; + + for (blk = 0; blk < blk_num; blk++){ + if (get_local_id(0) == 0){ + pos = factors[blk] << 16; + table[0] = pos; + for (j = 1; j < 16; j++){ + pos = (pos << 1) ^ ((pos >> 31) & 0x100B0000); + table[j] = pos; + } + } + barrier(CLK_LOCAL_MEM_FENCE); + + for (i = work_id; i < BLK_SIZE; i += work_size){ + pos = i & 255; + cache[pos] = src[i]; + barrier(CLK_LOCAL_MEM_FENCE); + + sum = 0; + sht = (i & 60) >> 2; + pos &= ~60; + for (j = 15; j >= 0; j--){ + mask = (table[j] << sht) >> 31; + sum ^= mask & cache[pos]; + pos += 4; + } + dst[i] ^= sum; + barrier(CLK_LOCAL_MEM_FENCE); + } + src += BLK_SIZE; + } +} diff --git a/source/par2j/verify.c b/source/par2j/verify.c new file mode 100644 index 0000000..694a96a --- /dev/null +++ b/source/par2j/verify.c @@ -0,0 +1,2931 @@ +// verify.c +// Copyright : 2022-10-14 Yutaka Sawada +// License : GPL + +#ifndef _UNICODE +#define _UNICODE +#endif +#ifndef UNICODE +#define UNICODE +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 // Windows Vista or later +#endif + +#include +#include +#include + +#include + +#include "common2.h" +#include "crc.h" +#include "md5_crc.h" +#include "ini.h" +#include "json.h" +#include "verify.h" + + +// 分割されたファイルを検索する為にファイル名を指定する +static void set_splited_filename( + wchar_t *find_name, // *filename* のフル・パス + wchar_t *file_name, // 本来のファイル名 filename.ext または filename + int dir_len) // フル・パスにおけるディレクトリ部分の長さ +{ + wchar_t *tmp_p; + + wcscpy(find_name, base_dir); + if (dir_len > base_len){ // サブ・ディレクトリが有る場合 + wcscpy(find_name + base_len, file_name); + find_name[dir_len] = '*'; + wcscpy(find_name + dir_len + 1, file_name + (dir_len - base_len)); + } else { // サブ・ディレクトリが無い場合 + find_name[base_len] = '*'; + wcscpy(find_name + base_len + 1, file_name); + } + tmp_p = wcsrchr(find_name + dir_len + 2, '.'); // ディレクトリ部分と先頭の「.」は無視する + if (tmp_p != NULL){ + if (wcslen(tmp_p) < EXT_LEN){ // 拡張子を取り除いて「*」に換える + *tmp_p = '*'; + tmp_p++; + *tmp_p = 0; + return; + } + } + wcscat(find_name, L"*"); +} + +// 文字化けしたファイル名のファイルを検索する為にファイル名を指定する +static int set_similar_filename( + wchar_t *find_name, // file*name.ext のフル・パス + wchar_t *file_name, // 本来のファイル名 file?name.ext + int dir_len) // フル・パスにおけるディレクトリ部分の長さ +{ + int i, len, count, ext_len; + + wcscpy(find_name, base_dir); + wcscpy(find_name + base_len, file_name); + + // ファイル名内の non-ASCII 文字の数 + count = 0; + i = 0; + ext_len = -1; + len = dir_len; // ディレクトリ部分の文字化けは考慮しない + while (find_name[len] != 0){ + if (find_name[len] >= 0x7F){ + count++; // non-ASCII 文字の数 + } else { + i++; // ASCII 文字の数 + if (find_name[len] == '.') + ext_len = len; // 拡張子の位置 + } + len++; + } + ext_len = len - ext_len; // 拡張子を含まない場合は元の文字数を超える + //printf("non = %d, ascii = %d, ext = %d\n", count, i, ext_len); + + // non-ASCII 文字を含まない場合は探さない + // 拡張子以外に ASCII 文字を含まない、または ASCII 文字が半分未満なら、探さない + if ((count == 0) || (i == ext_len) || (i < count)) + return 0; + + // non-ASCII 文字を「*」に置き換える + count = dir_len; + while (count < len){ + if (find_name[count] >= 0x80){ + if ((count > 0) && (find_name[count - 1] == '*')){ // 先の文字も「*」なら + // それ以降を前にずらす + for (i = count; i < len; i++) + find_name[i] = find_name[i + 1]; + len--; + count--; + } else { + find_name[count] = '*'; + } + } + count++; + } + + return 1; +} + +// 付加されてる部分が分割ファイルとして妥当か調べる +static int check_extra_part( + wchar_t *find_name, // 検出されたファイル名 + wchar_t *file_name, // 本来のファイル名 + int len) // 本来のファイル名の文字数 +{ + int num = (int)wcslen(find_name); + if (_wcsnicmp(find_name, file_name, len) == 0){ // 末尾に付加されてる + // 追加された部分の先頭が「.」か「_」以外なら無視する + if ((find_name[len] != '.') && (find_name[len] != '_')) + return 1; + + // 追加された部分が数値以外なら無視する + num = 0; + len++; + while (find_name[len] != 0){ + if ((find_name[len] < '0') || (find_name[len] > '9')) + return 1; + num = (num * 10) + (find_name[len] - '0'); // 番号を読み取る + len++; + } + if ((num > 99999) || (num < 0)) + return 1; // 数字がファイル分割の範囲外なら無視する + return 0; + + } else if ((num > len) && (_wcsnicmp(find_name + (num - len), file_name, len) == 0)){ // 先頭に付加されてる + if (num >= len + EXT_LEN) + return 1; // 追加された部分が長すぎる + // ファイル名の不一致部分の最後が「_」以外なら無視する + if (find_name[num - len - 1] != '_') + return 1; + return 0; + + } else { // それ以外の場合 + wchar_t name_part[MAX_LEN], ext_part[EXT_LEN], file_ext[EXT_LEN], *tmp_p; + + wcscpy(name_part, find_name); + ext_part[0] = 0; + tmp_p = wcsrchr(name_part, '.'); + if (tmp_p != NULL){ + if (wcslen(tmp_p) < EXT_LEN){ + wcscpy(ext_part, tmp_p); // 拡張子を記録しておく + *tmp_p = 0; // 拡張子を取り除く + } + } + file_ext[0] = 0; + tmp_p = wcsrchr(file_name, '.'); + if (tmp_p != NULL){ + if (wcslen(tmp_p) < EXT_LEN) + wcscpy(file_ext, tmp_p); // 拡張子を記録しておく + } + len -= (int)wcslen(file_ext); // 本来のファイル名の長さから、拡張子の長さを引く + //printf("name length = %d \n", len); + + // 拡張子が一致しない、または追加された拡張子が数値以外の場合は無視する + if (_wcsicmp(file_ext, ext_part) != 0){ + if (ext_part[0] == 0) + return 1; // 拡張子が無ければ駄目 + + // 拡張子が数値以外なら無視する + num = 0; + tmp_p = ext_part + 1; + while (*tmp_p != 0){ + if ((*tmp_p < '0') || (*tmp_p > '9')) + return 1; + num = (num * 10) + (*tmp_p - '0'); // 番号を読み取る + tmp_p++; + } + if ((num > 99999) || (num < 0)) + return 1; // 数字がファイル分割の範囲外なら無視する + + // 拡張子が数字になっても、それ以外が完全に一致すれば許容する + if ((name_part[len] == 0) && (_wcsnicmp(find_name, file_name, len) == 0)) + return 0; + + // 更に前の拡張子を捜す + tmp_p = wcsrchr(name_part, '.'); + if (tmp_p != NULL){ + if (wcslen(tmp_p) < EXT_LEN){ + wcscpy(ext_part, tmp_p); // 拡張子を記録しておく + *tmp_p = 0; // 拡張子を取り除く + } + } else { + return 1; + } + if (_wcsicmp(file_ext, ext_part) != 0) + return 1; // それも一致しなければ駄目 + } + + // ファイル名がどこまで一致するか調べる + if (_wcsnicmp(find_name, file_name, len) == 0){ // 拡張子との間にだけ付加されてる + // ファイル名の不一致部分の最初が「.」と「_」以外なら無視する + if ((name_part[len] != '.') && (name_part[len] != '_')) + return 1; + // ファイル名の不一致部分の最初以降に「.」を含むなら無視する + if (wcschr(name_part + (len + 1), '.') != NULL) + return 1; + + // 拡張子と付加部分が両方とも数値だけなら、末尾への追加と判定する + len++; + while (name_part[len] != 0){ + if ((name_part[len] < '0') || (name_part[len] > '9')){ + len = 0; + break; + } + len++; + } + if (len > 0){ // 付加部分が数値のみ + len = 1; + while (file_ext[len] != 0){ + if ((file_ext[len] < '0') || (file_ext[len] > '9')){ + len = 0; + break; + } + len++; + } + if (len > 0) // 拡張子が数値のみ + return 1; + } + return 0; + + } else { // 何箇所かに付加されてる + wchar_t name_lower[MAX_LEN]; + int i, j; + // 比較用に小文字にする + wcscpy(name_lower, file_name); + _wcslwr(name_lower); + if (file_ext[0] != 0) + wcscat(name_part, file_ext); // 拡張子を戻す + _wcslwr(name_part); + // どのくらい一致するかを調べる + num = 0; + i = 0; + j = 0; + while ((name_part[i] != 0) && (name_lower[j] != 0)){ + if (name_part[i] == name_lower[j]){ + num++; + i++; + j++; + } else if (name_lower[j] >= 0x7F){ // 本来のファイル名に non-ASCII 文字があればとばす + j++; + } else { + i++; + } + } + //printf("match = %d, i = %d, j = %d\n", num, i, j); + if ((num * 2 < i) || (num * 2 < j)) + return 1; // 一致部分が半分よりも少ないと駄目 + return 0; + } + } +} + +// 破損したソース・ファイルの作業ファイルを無視する +static int avoid_temp_file( + wchar_t *comp_path, // 比較するファイルのパス + file_ctx_r *files) // 各ソース・ファイルの情報 +{ + int i, len, len2; + + len2 = (int)wcslen(comp_path); + // 作業ファイルの末尾は決まってる wcslen(L"_par.tmp") = 8 + if (len2 <= 8) + return 0; + if (wcscmp(comp_path + (len2 - 8), L"_par.tmp") != 0) + return 0; + + // ソース・ファイルごとに比較する + for (i = 0; i < entity_num; i++){ + // 空のファイルやフォルダは作業ファイルが存在しない + if (files[i].size == 0) + continue; + // 完全・追加・別名・移動のファイルは作業ファイルが存在しない + if ((files[i].state & 3) == 0) + continue; + // 上書き修復する場合は作業ファイルが存在しない + if (files[i].state & 4) + continue; + + len = (int)wcslen(list_buf + files[i].name); + if ((len2 == len + 8) && (_wcsnicmp(list_buf + files[i].name, comp_path, len) == 0)) + return 1; // 作業ファイル + } + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ソート時に項目を比較する +static int sort_cmp_crc(const void *elem1, const void *elem2) +{ + unsigned int crc1, crc2; + + crc1 = ((unsigned int *)elem1)[1]; + crc2 = ((unsigned int *)elem2)[1]; + + if (crc1 < crc2) + return -1; + if (crc1 > crc2) + return 1; + return 0; +} + +// 二分探索法を改造して同じキーに対応する +static int binary_search( + int *order, // 配列 + int high, // 配列の要素数 = 探索範囲の上限要素 +1 + unsigned int key, // 目的の値 + source_ctx_r *s_blk) +{ + int mid, low = 0; // 探索範囲の下限要素 + + while (low < high){ + mid = (low + high) >> 1; // 配列の中央位置を計算 + if (s_blk[order[mid]].crc > key){ // 目的の値の方が小さい + high = mid; // 上限位置を変更する + } else if (s_blk[order[mid]].crc < key){ // 目的の値の方が大きい + low = mid + 1; // 下限位置を変更する + } else { // 目的の値と一致 + // 同じ key のブロックが複数存在するかもしれないので + while ((mid > low) && (s_blk[order[mid - 1]].crc == key)) + mid--; + return mid; + } + } + + return 0x00FFFFFF; // 見つからなかった +} + +// 二分探索法を改造して一致しない場合は high 側を返す +static int binary_search_high( + unsigned int *keys, // 配列 + int low, // 探索範囲の下限要素 + int high, // 配列の要素数、探索範囲の上限要素+1 + unsigned int key) // 目的の値 +{ + int mid; + + while (low < high){ // 下限位置の方が上限位置を超えたら、目的の値は存在しない + mid = (low + high) >> 1; // 配列の中央位置を計算 + if (keys[mid] > key){ // 目的の値の方が小さい + high = mid; // 上限位置を変更する + } else if (keys[mid] < key){ // 目的の値の方が大きい + low = mid + 1; // 下限位置を変更する + } else { // 目的の値と一致 + // 同じ key のブロックが複数存在するかもしれないので + while ((mid > low) && (keys[mid - 1] == key)) + mid--; + return mid; + } + } + + return high; // 見つからなかった場合は、次に大きい位置を返す +} + +// ソートされたデータに対して一定間隔ごとにインデックスを作る。 +// 目的の値 -> インデックスで直前の値 -> 線形探索 で一致するかどうか +static void init_index_search( + unsigned int *keys, // データの配列 + int data_count, // データ数 + int *index, // 目次 + int index_bit) // 目次の大きさ、(32-bit値 >> shift) = 目次の項目 +{ + int i, off = 0; + + for (i = 0; i < (1 << index_bit); i++){ + off = binary_search_high(keys, off, data_count, i << (32 - index_bit)); + index[i] = off; + } +} + +// 目次の大きさによって探索速度が変わる。 +static int index_search( + unsigned int *keys, // 配列 + int data_count, // 配列の要素数 = 探索範囲の上限要素 +1 + unsigned int key, // 目的の値 + int *index, // 目次 + int index_shift) // (32-bit値 >> shift) = 目次の項目 +{ + int i; + + i = index[key >> index_shift]; // 配列内のどこにあるか + // リニア・サーチで残りから探す + while (i < data_count){ + if (keys[i] > key) + return 0x00FFFFFF; // 上回るなら目的の値は存在しない + if (keys[i] == key) + return i; + i++; + } + return 0x00FFFFFF; // 見つからなかった +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 読み込み用のサブ・スレッド +static DWORD WINAPI thread_read(LPVOID lpParameter) +{ + unsigned char *buf; + unsigned int rv; + slice_ctx *sc; + + sc = (slice_ctx *)lpParameter; + buf = sc->buf + (block_size * 2); + + WaitForSingleObject(sc->run, INFINITE); // 読み込み開始の合図を待つ + while (sc->size){ // 読みこみエラーが発生してもループから抜けない + // ファイルから読み込む + if (!ReadFile(sc->hFile, buf, sc->size, &rv, NULL)){ + print_win32_err(); // 0x17 = ERROR_CRC, 0x21 = ERROR_LOCK_VIOLATION + //memset(buf, 0, sc->size); // 読み込み時にエラーが発生した部分は 0 にしておく + sc->size = 0xFFFFFFFF; // それ以上の検査を中止する + } else if (sc->size < rv){ + memset(buf + rv, 0, sc->size - rv); // 指定サイズを読み込めなかった分は 0 にしておく + // エラーではない?ので続行する + } + //_mm_sfence(); // メモリーへの書き込みを完了する + SetEvent(sc->end); // 読み込み終了を通知する + WaitForSingleObject(sc->run, INFINITE); // 読み込み開始の合図を待つ + } + + // 終了処理 + CloseHandle(sc->run); + CloseHandle(sc->end); + return 0; +} + +// スライス検査の準備をする +// -1=スライス検査できない、または不要, 0=準備完了, 1=スライド検査せず +int init_verification( + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc) +{ + unsigned char *short_use; + int i, j, num, file_block, block_count, index_bit, short_count; + int *order = NULL, *short_crcs; + unsigned int last_size; +// unsigned int time_last; + + // 作業ファイル用 + sc->num = -1; + sc->hFile_tmp = NULL; + + // スライド検索するスライスが何個存在するか + j = 0; + block_count = 0; + short_count = 0; + sc->min_size = block_size; + for (num = 0; num < entity_num; num++){ + if ((files[num].size > 0) && ((files[num].state & 0x80) == 0)){ // チェックサムがあるファイルだけ比較する + file_block = (int)(files[num].size / (__int64)block_size); // フルサイズのブロックの数 + block_count += file_block; + last_size = (unsigned int)(files[num].size - ((__int64)file_block * (__int64)block_size)); + if (last_size > 0){ + short_count++; // 半端なブロックを含む + if ((sc->min_size > last_size) && (sc->min_size > 4)) // 4バイトより大きければ + sc->min_size = last_size; + } + if (files[num].state & 0x03) // 消失 0x01、破損 0x02 ならスライスを探す + j++; // 検査する必要があるソース・ファイルの数 + } + } + //printf("target = %d, full = %d, short = %d, min_size = %d\n", j, block_count, short_count, sc->min_size); + if (j == 0) + return -1; // 完全またはチェックサムが無いので、スライスを探さなくていい + if (switch_v & 4) + return 0; // 順列検査ではスライド検査しない + if (block_count == 0) + return 1; // スライド検査する必要なし、簡易検査で十分 + sc->block_count = block_count; + sc->short_count = short_count; + + // インデックス・サーチで使う目次 + index_bit = 4; // 目次の大きさは ブロック数 / 5~8 にする + while ((1 << (index_bit + 3)) < block_count) + index_bit++; + // CRC-32 の順序を格納するバッファーを確保する + i = sizeof(int) * ((block_count * 2) + (1 << index_bit)); + //printf("index_bit = %d (%d), CRC buffer size = %d\n", index_bit, 1 << index_bit, i); + if (short_count > 0){ + i += sizeof(int) * entity_num + ((entity_num + 3) & ~3); // short_crc 用の領域 + } + order = (int *)malloc(i); + if (order == NULL) + return -1; // CRC-32 すら比較できないので簡易検査もできない + j = 0; + for (num = 0; num < entity_num; num++){ + if ((files[num].state & 0x80) == 0){ // チェックサムがあるファイルだけ比較する + file_block = (int)(files[num].size / (__int64)block_size); // フルサイズのブロックの数 + for (i = files[num].b_off; i < files[num].b_off + file_block; i++){ + order[j * 2 ] = i; // 最初はブロック番号にしておく + order[j * 2 + 1] = s_blk[i].crc; + j++; + } + } + } +// for (j = 0; j < block_count; j++) +// printf("order[%3d] = %3d, %08x\n", j, order[j * 2], order[j * 2 + 1]); + + // 昇順に並び替える + qsort(order, block_count, sizeof(int) * 2, sort_cmp_crc); + // [番号, CRC] の順を並び替えて [番号] だけにする + for (i = 1; i < block_count; i++) + order[i] = order[i * 2]; // 番号を前に移動させる + // 並び替えられた CRC も記録しておく + for (i = 0; i < block_count; i++){ + order[block_count + i] = s_blk[order[i]].crc; // 並び替えた後の順序にする + //printf("crc[%2d]: Block[%2d], 0x%08X\n", i, order[i], s_blk[order[i]].crc); + } + // インデックス・サーチ用の目次を作る + init_index_search(order + block_count, block_count, order + (block_count * 2), index_bit); + sc->index_shift = 32 - index_bit; + sc->order = order; + + // スライド検査しないならここまで + if (switch_v & 1) + return 0; + + // 詳細検査用の作業領域を確保する + //printf("\n block_size = %d, allocation size = %I64d\n", block_size, (__int64)block_size * 3); + if ((size_t)block_size * 3 < get_mem_size(0)) + sc->buf = (unsigned char *)malloc((size_t)block_size * 3); + if (sc->buf == NULL) + return 1; // メモリー不足でスライド検査できない、簡易検査にする + +//time_last = GetTickCount(); + if (short_count > 0){ // 半端なブロックを比較する準備 + short_crcs = order + ((block_count * 2) + (1 << index_bit)); + short_use = (unsigned char *)(short_crcs + entity_num); + for (num = 0; num < entity_num; num++){ + short_use[num] = 0; // チェックサムなし、または半端なブロックでない + if ((files[num].state & 0x80) == 0){ // チェックサムがあるファイルだけ比較する + last_size = (unsigned int)(files[num].size % (__int64)block_size); + if (last_size > 0){ + //file_block = (int)(files[num].size / (__int64)block_size); // フルサイズのブロックの数 + //j = files[num].b_off + file_block; // 末尾ブロックの番号 + //printf("block[%4d] : size = %d, pad = %d\n", j, s_blk[j].size, block_size - s_blk[j].size); + // ブロック・サイズが大きいとパディング部分を取り除くのに時間がかかるから、必要になってから行う + //short_crcs[num] = crc_reverse_zero(s_blk[j].crc, block_size - s_blk[j].size); + if (files[num].b_num > 1){ + short_use[num] = 2; // 末尾の半端なブロック + } else { + short_use[num] = 1; // 小さなファイル + } + } + } + } + } +//time_last = GetTickCount() - time_last; +//printf("CRC setup: %d mil sec\n", time_last); + + // 読み込み用スレッドの準備をする + sc->run = CreateEvent(NULL, FALSE, FALSE, NULL); // 両方とも Auto Reset にする + if (sc->run == NULL){ + free(sc->buf); + sc->buf = NULL; + return 1; + } + sc->end = CreateEvent(NULL, FALSE, FALSE, NULL); + if (sc->end == NULL){ + free(sc->buf); + sc->buf = NULL; + CloseHandle(sc->run); + sc->run = NULL; + return 1; + } + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する + sc->h = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_read, (LPVOID)sc, 0, NULL); + if (sc->h == NULL){ + free(sc->buf); + sc->buf = NULL; + CloseHandle(sc->run); + CloseHandle(sc->end); + sc->run = NULL; + sc->end = NULL; + return 1; + } + + return 0; +} + +// スライス断片を比較用にメモリー上に一時保管する +void save_flake( + unsigned char *buf, + int id, // ブロック番号 + int flake_size, // 断片のサイズ + int side, // どちら側か 1=front, 0=rear + slice_ctx *sc) +{ + unsigned char *p; + int i, rv; + size_t mem_size; + flake_ctx *fc; + + rv = -1; // 拡張するかどうか (空き領域の番号) + if (sc->flake_count > 0){ // 保管場所があるか調べる + p = sc->flk_buf; + for (i = 0; i < sc->flake_count; i++){ + fc = (flake_ctx *)p; + p += sizeof(flake_ctx); + if (fc->id == id){ // 既にスライス断片が記録されてる + //printf("flake[%d] = %d, size = %d, add %d ok\n", i, id, flake_size, side); + if (side){ // 前半に上書きコピーする + memcpy(p, buf, flake_size); + fc->front_size = flake_size; + } else { // 後半に上書きコピーする + memcpy(p + (block_size - flake_size), buf, flake_size); + fc->rear_size = flake_size; + } + return; // 追加して終わる + } else if (fc->id == -1){ // 空き領域が存在する + rv = i; // 拡張する必要なし + } + p += block_size; // 次の保管領域へ + } + } + + if (rv == -1){ // スライス断片の記録領域を拡張する + int max_count; + mem_size = get_mem_size(0) / 2; // 使えるサイズの半分までにする + max_count = (int)(mem_size / (sizeof(flake_ctx) + (size_t)block_size)); + i = (source_num - entity_num) / 2; // 原理的に分割断片は「ソース・ブロック数 - ファイル数」の半分以下 + if (max_count > i) + max_count = i; + i = source_num - first_num; // 未発見のソース・ブロックの数までにする + if (max_count > i) + max_count = i; + //printf("mem_size = %zu MB, max_count = %d, flake_count = %d \n", mem_size >> 20, max_count, sc->flake_count); + if (sc->flake_count < max_count){ + mem_size = sc->flake_count + 1; + mem_size *= sizeof(flake_ctx) + (size_t)block_size; + p = (unsigned char *)realloc(sc->flk_buf, mem_size); + if (p != NULL){ + sc->flk_buf = p; + rv = sc->flake_count; // 新規作成した領域の番号 + sc->flake_count += 1; + } + } + if (rv == -1){ + if (sc->flake_count > 0){ // これ以上拡張できないなら末尾に入れる + rv = 0; // 先頭を空けてずらす + } else { // 領域自体を確保できない場合は保管しない + sc->flake_count = -1; + return; + } + } + } + if (rv < sc->flake_count - 1){ // 末尾領域以外が空いてるなら + p = sc->flk_buf + (sizeof(flake_ctx) + (size_t)block_size) * rv; + mem_size = (sizeof(flake_ctx) + (size_t)block_size) * (sc->flake_count - rv - 1); + //printf("slide from %p to %p, %zu bytes \n", p + sizeof(flake_ctx) + block_size, p, mem_size); + memmove(p, p + sizeof(flake_ctx) + block_size, mem_size); + rv = sc->flake_count - 1; // 前にずらして末尾領域に保管する + } + + //printf("flake[%d] = %d, size = %u, save %d ok\n", rv, id, flake_size, side); + p = sc->flk_buf + (sizeof(flake_ctx) + (size_t)block_size) * rv; + fc = (flake_ctx *)p; + p += sizeof(flake_ctx); + if (side){ // 前半に上書きコピーする + memcpy(p, buf, flake_size); + fc->front_size = flake_size; + fc->rear_size = 0; + } else { // 後半に上書きコピーする + memcpy(p + (block_size - flake_size), buf, flake_size); + fc->front_size = 0; + fc->rear_size = flake_size; + } + fc->id = id; +} + +// 保管されてるスライス断片を調べて完全なスライスを探す +int check_flake( + wchar_t *temp_path, // 作業用、基準ディレクトリが入ってる + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc) +{ + unsigned char *p, err_mag; + int i, j, rv, num, err_off; + flake_ctx *fc; + + p = sc->flk_buf; + for (j = 0; j < sc->flake_count; j++){ + fc = (flake_ctx *)p; + p += sizeof(flake_ctx); + i = fc->id; + if (i != -1){ // スライス断片が記録されてる + if (s_blk[i].exist != 0){ // ブロックの断片を保管した後に、そのブロックが発見されていたら + fc->id = -1; // 不要になった領域を空ける + //printf("flake[%d] = %d, release\n", j, i); + // 両側のスライス断片が揃ってる場合だけ、スライス断片の合計がブロック・サイズ以上になる + } else if ((unsigned int)(fc->front_size) + (unsigned int)(fc->rear_size) >= block_size){ + // 分割されてるか試すスライスはブロック・サイズのものだけ + rv = correct_error(p, s_blk[i].size, s_blk[i].hash, s_blk[i].crc, &err_off, &err_mag); + //printf("flake[%d] = %d, check = %d\n", j, i, rv); + if (rv >= 0){ // 完全なブロックを新たに発見した (発見済みは先に除外してる) + if (switch_v & 16){ // コピーする + num = s_blk[i].file; + if (open_temp_file(temp_path, num, files, sc)) + return 1; + if (file_write_data(sc->hFile_tmp, + (__int64)(i - files[num].b_off) * (__int64)block_size, p, s_blk[i].size)){ + printf("file_write_data, %d\n", i); + return 1; + } + } + s_blk[i].exist = 0x1002; // このファイルで断片が一致した印 + first_num++; + } + fc->id = -1; // 結果にかかわらず、比較が終わったら領域を空ける + } + } + p += block_size; // 次の保管領域へ + } + + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define MISS_LIMIT 6 + +// ソース・ブロックのチェックサムを調べる +// ファイル番号が既知な場合は、前後に同時にゴミが付かなければ位相のずれにも対応する +// ブロック単位での読み込みエラーを無視して継続する +// -2=キャンセル, -1=エラー, 0=見つからない, 1~=見つかった個数 +static int search_block_simple( + wchar_t *temp_path, // 作業用、基準ディレクトリが入ってる + int num1, // file_ctx におけるファイル番号 (未知なら -1) + unsigned int last_size, // 末尾ブロックのサイズ + wchar_t *file_name, // 表示するファイル名 + HANDLE hFile, // ファイルのハンドル + __int64 file_size, // 存在するファイルのサイズ + __int64 last_off, // どこから検査を開始するか (ここまでは既に検査済み) + int flag_split, // 0以外 = 後方からは検索しない + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc) +{ + unsigned char hash[20]; + int i, j, b_last, find_num, find_flag, find_next, short_next; + int block_count, num, i1, i2, i3, i4, miss_num, miss_max; + int *order, *index, index_shift; + unsigned int crc, *crcs; + unsigned int time_last; + __int64 file_off, short_off; + + if (file_size < last_off + last_size) + return 0; // 小さすぎるファイルは調べない + block_count = sc->block_count; + miss_num = 0; // 何連続で検出できなかったか + miss_max = (int)((file_size >> 4) / (__int64)block_size); // size/16 ブロック個まで (6%) + if (miss_max < MISS_LIMIT) + miss_max = MISS_LIMIT; + find_num = 0; // このファイル内で何ブロック見つけたか + find_next = -1; // 次に見つかると予想したブロックの番号 + short_next = -1; + if (num1 >= 0){ // ファイル番号が指定されてるなら + b_last = files[num1].b_off + files[num1].b_num - 1; // 末尾ブロックの番号 + find_next = (int)(last_off / (__int64)block_size); // 検査開始位置のブロック番号 + find_next += files[num1].b_off; + if ((last_off < files[num1].size) && // ファイル本来のサイズよりも小さいなら、末尾ブロックは未検出 + (last_size < block_size)){ // 末尾の半端なブロックの番号と想定位置 + short_next = b_last; + if (find_next == short_next) + find_next = -1; // 予想が重複したら末尾ブロックとして探す + short_off = files[num1].size - last_size; + } + //printf("file = %d, find_next = %d\n", num1, find_next); + //printf("short_off = %I64d, short_next = %d\n", short_off, short_next); + } else if (file_size < (__int64)block_size){ // ブロック・サイズよりも小さければ + // ファイル・サイズ以下の半端なスライスを含むファイルを探す + find_flag = -1; + for (num = 0; num < entity_num; num++){ // ファイル番号 + if (files[num].size == file_size){ // サイズが同じなら + // ハッシュ値を計算する + if (find_flag < 0){ + if (file_md5_crc32_block(hFile, 0, (unsigned int)(files[num].size), hash) != 0) + break; // エラーなら比較を終了する + find_flag = 0; + } + if (find_flag == 0){ + i = files[num].b_off; + if (memcmp(hash, s_blk[i].hash, 20) == 0){ // チェックサムが一致するか確かめる + if (s_blk[i].exist == 0){ // 未発見のブロックなら + if (switch_v & 16){ // コピーする + if (open_temp_file(temp_path, num, files, sc)) + return -1; + if (file_copy_data(hFile, 0, sc->hFile_tmp, 0, s_blk[i].size)){ + printf("file_copy_data, %d\n", i); + return -1; + } + } + s_blk[i].exist = 2; + first_num++; + } + find_num++; + write_ini_verify(i, 0, 0); + s_blk[i].exist |= 0x1000; // このファイル内で見つけた印 + //printf("file size = %I64d, slice size = %d, T %d, F %d\n", file_size, s_blk[i].size, i, num); // Tiny + break; + } + } + } + } + return find_num; +/* 複数のファイルと比較すると重くなるかも? + crc = 0; + for (num = 0; num < entity_num; num++){ // ファイル番号 + if (files[num].size <= file_size){ + // ハッシュ値を計算する + if (crc != (unsigned int)(files[num].size)){ + if (file_md5_crc32_block(hFile, 0, (unsigned int)(files[num].size), hash) != 0) + break; // エラーなら比較を終了する + crc = (unsigned int)(files[num].size); // 計算した際のサイズを記録しておく + } + if (crc == (unsigned int)(files[num].size)){ + i = files[num].b_off; + if (memcmp(hash, s_blk[i].hash, 20) == 0){ // チェックサムが一致するか確かめる + if (s_blk[i].exist == 0){ // 未発見のブロックなら + if (switch_v & 16){ // コピーする + if (open_temp_file(temp_path, num, files, sc)) + return -1; + if (file_copy_data(hFile, 0, sc->hFile_tmp, 0, s_blk[i].size)){ + printf("file_copy_data, %d\n", i); + return -1; + } + } + s_blk[i].exist = 2; + first_num++; + } + find_num++; + write_ini_verify(i, 0, 0); + s_blk[i].exist |= 0x1000; // このファイル内で見つけた印 + //printf("file size = %I64d, slice size = %d, T %d, F %d\n", file_size, s_blk[i].size, i, num); // Tiny + } + } + } + } + return find_num; +*/ + } + // インデックス・サーチ用 + order = sc->order; + crcs = order + block_count; + index = crcs + block_count; + index_shift = sc->index_shift; + time_last = GetTickCount(); + + // ブロック・サイズごとに探す + if (block_count > 0){ + // 前から順にチェックサムを比較する + file_off = last_off; // 検査開始位置から調べる + //printf("search from %I64d, file %d, next = %d\n", file_off, num1, find_next); + while (file_off + (__int64)block_size <= file_size){ + find_flag = -2; + // 次の番号のブロックがその位置にあるかを先に調べる (発見済みでも) + if ((short_next >= 0) && (file_off == short_off)){ + i = short_next; + if (file_md5_crc32_block(hFile, file_off, s_blk[i].size, hash) != 0){ + return find_num; // エラーなら検査を終了する + } else { + if (memcmp(hash, s_blk[i].hash, 20) == 0) // チェックサムが一致するか確かめる + find_flag = 0; + } + } + if (find_flag < 0){ + if (file_md5_crc32_block(hFile, file_off, block_size, hash) != 0) + return find_num; // エラーなら検査を終了する + if ((find_next >= 0) && (memcmp(hash, s_blk[find_next].hash, 20) == 0)){ // チェックサムが一致するか確かめる + i = find_next; + find_flag = 3; + } + if (find_flag < 0){ // 予想したブロックが見つからなかった場合は全てのブロックと比較する + memcpy(&crc, hash + 16, 4); + crc ^= window_mask; // 記録されてるチェックサムから初期値と最終処理の 0xFFFFFFF を取り除く + // CRC-32 が一致する番号 +// j = binary_search(order, block_count, crc, s_blk); +// j = index_search(crcs, block_count, crc, index, index_shift); + j = 0x00FFFFFF; // 見つからなかった + i = index[crc >> index_shift]; // 配列内のどこにあるか + // リニア・サーチで残りから探す + while (i < block_count){ + if (crcs[i] > crc) + break; // 上回るなら目的の値は存在しない + if (crcs[i] == crc){ + j = i; + break; + } + i++; + } + // 一致するブロックを優先度別に記録する + i1 = i2 = i3 = i4 = -1; + for (; j < block_count; j++){ // 複数ブロックが一致するかもしれないので + if (crcs[j] != crc) + break; // CRC-32 が一致しなくなったら抜ける + i = order[j]; + if (memcmp(hash, s_blk[i].hash, 20) == 0){ // チェックサムが一致するか確かめる + find_flag = 4; + if ((s_blk[i].exist & 0x1000) == 0){ // このファイル内で未発見のブロックを優先的に探す (内容が重複してる場合に順序を保つ為) + if (s_blk[i].file == num1){ // 指定されたファイル番号のブロック + i1 = i; + break; // 最優先のブロックを見つけた場合は直ちにループから抜ける + } else { + i2 = i; + if (num1 < 0) + break; + } + } else { // このファイル内で発見済みのブロックも比較する (内容をコピーして追加した改変を認識する) + if (s_blk[i].file == num1){ + i3 = i; + } else { + i4 = i; + } + } + } + } + // 最も優先度の高いブロックとして認識する + if (i1 >= 0){ + i = i1; // 指定されたファイル番号で未発見のブロック + } else if (i2 >= 0){ + i = i2; // 他のファイル番号で未発見のブロック + } else if (i3 >= 0){ + i = i3; // 指定されたファイル番号で発見済みのブロック + } else if (i4 >= 0){ + i = i4; // 他のファイル番号で発見済みのブロック + } + } + } + if (find_flag >= 0){ // その位置でブロックのチェックサムが一致すれば + num = s_blk[i].file; // 見つけたブロックが属するファイル番号 + if (s_blk[i].exist == 0){ // 未発見のブロックなら + if (switch_v & 16){ // コピーする + if (open_temp_file(temp_path, num, files, sc)) + return -1; + if (file_copy_data(hFile, file_off, sc->hFile_tmp, + (__int64)(i - files[num].b_off) * (__int64)block_size, s_blk[i].size)){ + printf("file_copy_data, %d\n", i); + return -1; + } + } + s_blk[i].exist = 2; + first_num++; + } + if ((s_blk[i].exist & 0x1000) == 0){ // 同一ファイルで重複していても一回だけ記録する + find_num++; + write_ini_verify(i, 0, file_off); + } + s_blk[i].exist |= 0x1000; // このファイル内で見つけた印 +/* if (find_flag == 0){ + printf("%9I64d : L %d, F %d\n", file_off, i, s_blk[i].file); // Last + } else if (find_flag == 3){ + printf("%9I64d : N %d, F %d\n", file_off, i, s_blk[i].file); // Next + } else { + printf("%9I64d : S %d, F %d\n", file_off, i, s_blk[i].file); // Search + }*/ + // 次に見つかるブロックを予測する + find_next = i + 1; + if (find_next >= source_num){ // 最後までいった + short_next = -1; + find_next = -1; + } else if (s_blk[find_next].file != num){ // 異なるファイルは連続しない + short_next = -1; + find_next = -1; + } else if (s_blk[find_next].size < block_size){ // 半端なブロックは別に調べる + short_next = find_next; + short_off = file_off + block_size; + //printf("short_off = %I64d, short_next = %d, file = %d\n", short_off, short_next, num); + find_next = -1; + } else { + short_next = files[num].b_off + files[num].b_num - 1; + if (s_blk[short_next].size < block_size){ // 半端なブロックは別に調べる + short_off = file_off + (__int64)(short_next - i) * (__int64)block_size; + //printf("short_off = %I64d, short_next = %d, file = %d\n", short_off, short_next, num); + } else { + short_next = -1; + } + } + file_off += s_blk[i].size; + last_off = file_off; // 最後に見つけたブロックの終端 + miss_num = 0; + } else { + file_off += block_size; + miss_num++; + //printf("miss = %d / %d\n", miss_num, miss_max); + if (miss_num >= miss_max) + break; + } + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress_file((int)((file_off * 1000) / file_size), first_num, file_name)) + return -2; + time_last = GetTickCount(); + } + } + } + // 末尾の半端なブロックの検査 + if ((short_next >= 0) && (short_off + s_blk[short_next].size <= file_size) && ((s_blk[short_next].exist & 0x1000) == 0)){ + file_off = short_off; + i = short_next; + if (file_md5_crc32_block(hFile, file_off, s_blk[i].size, hash) != 0){ + return find_num; // エラーなら検査を終了する + } else { + if (memcmp(hash, s_blk[i].hash, 20) == 0){ // チェックサムが一致するか確かめる + num = s_blk[i].file; // 見つけたブロックが属するファイル番号 + if (s_blk[i].exist == 0){ // 未発見のブロックなら + if (switch_v & 16){ // コピーする + if (open_temp_file(temp_path, num, files, sc)) + return -1; + if (file_copy_data(hFile, file_off, sc->hFile_tmp, + (__int64)(i - files[num].b_off) * (__int64)block_size, s_blk[i].size)){ + printf("file_copy_data, %d\n", i); + return -1; + } + } + s_blk[i].exist = 2; + first_num++; + } + if ((s_blk[i].exist & 0x1000) == 0){ // 同一ファイルで重複していても一回だけ記録する + find_num++; + write_ini_verify(i, 0, file_off); + } + s_blk[i].exist |= 0x1000; // このファイル内で見つけた印 + //printf("%9I64d : L2 %d, F %d\n", file_off, i, s_blk[i].file); // Last + } + } + } + if ((num1 >= 0) && (files[num1].b_num >= 2) && (file_size >= last_size) && ((s_blk[b_last].exist & 0x1000) == 0)){ + if (file_size >= files[num1].size){ // 本来のサイズよりも大きいなら + file_off = files[num1].size - last_size; + } else if (file_size >= (__int64)block_size + last_size){ // ブロック倍よりも大きいなら + file_off = last_off; + } else { // 末尾ブロックのサイズなら + file_off = 0; + } + if (file_md5_crc32_block(hFile, file_off, last_size, hash) != 0){ + return find_num; // エラーなら検査を終了する + } else { + i = b_last; + if (memcmp(hash, s_blk[i].hash, 20) == 0){ // チェックサムが一致するか確かめる + if (s_blk[i].exist == 0){ // 未発見のブロックなら + if (switch_v & 16){ // コピーする + if (open_temp_file(temp_path, num1, files, sc)) + return -1; + if (file_copy_data(hFile, file_off, sc->hFile_tmp, file_off, last_size)){ + printf("file_copy_data, %d\n", i); + return -1; + } + } + s_blk[i].exist = 2; + first_num++; + } + find_num++; + write_ini_verify(i, 0, file_off); + s_blk[i].exist |= 0x1000; // このファイル内で見つけた印 + //printf("%9I64d : L1 %d, F %d\n", file_off, i, s_blk[i].file); // Last + } + } + } + + // ファイル番号が指定されてない場合は後方からの検索をしない + // 最後に見つかったブロックがファイルの終端なら、それ以上の検査をしない + if ((num1 < 0) || (file_size <= last_off + last_size)) + return find_num; + // 指定されたファイルと同じサイズなら、位相のずれは無いとみなす + if ((miss_num < miss_max) && (file_size == files[num1].size)) + return find_num; + // 分割ファイルなら後方からの検索をしない (類似名ファイルとはサイズで区別する) + if ((flag_split != 0) && (file_size <= files[num1].size / 2)) + return find_num; + + // 末尾ブロックの検査 + file_off = file_size - last_size; // 末尾にあるはずの終端ブロックの位置 + miss_num = 0; + if ((s_blk[b_last].exist & 0x1000) == 0){ + miss_num = 1; + if (file_off >= last_off){ + if (file_md5_crc32_block(hFile, file_off, last_size, hash) != 0) + return find_num; // エラーなら検査を終了する + i = b_last; + if (memcmp(hash, s_blk[i].hash, 20) == 0){ // チェックサムが一致するか確かめる + if (s_blk[i].exist == 0){ // 未発見のブロックなら + if (switch_v & 16){ // コピーする + if (open_temp_file(temp_path, num1, files, sc)) + return -1; + if (file_copy_data(hFile, file_off, sc->hFile_tmp, files[num1].size - last_size, last_size)){ + printf("file_copy_data, %d\n", i); + return -1; + } + } + s_blk[i].exist = 2; + first_num++; + } + find_num++; + write_ini_verify(i, 0, file_off); + s_blk[i].exist |= 0x1000; // このファイル内で見つけた印 + //printf("%9I64d : RL %d, F %d\n", file_off, i, s_blk[i].file); // Last + miss_num = 0; + } + } + } + // ファイル内にブロックが複数あるなら遡って探す + if (files[num1].b_num >= 2){ // 常に block_count > 0 になる + file_off -= block_size; // 一個前の位置にしておく + //printf("sencond search from = %I64d to %I64d\n", file_off, last_off); + find_next = b_last - 1; + while (file_off > last_off){ // 前方からの検索で見つけた最後のブロックまで + find_flag = -2; + // 次の番号のブロックがその位置にあるかを先に調べる (発見済みでも) + if (file_md5_crc32_block(hFile, file_off, block_size, hash) != 0){ + return find_num; // エラーなら検査を終了する + } else { + if ((find_next >= 0) && (memcmp(hash, s_blk[find_next].hash, 20) == 0)){ // チェックサムが一致するか確かめる + i = find_next; + find_flag = 3; + } + if (find_flag < 0){ + memcpy(&crc, hash + 16, 4); + crc ^= window_mask; // 記録されてるチェックサムから初期値と最終処理の 0xFFFFFFF を取り除く + // CRC-32 が一致する番号 +// j = binary_search(order, block_count, crc, s_blk); +// j = index_search(crcs, block_count, crc, index, index_shift); + j = 0x00FFFFFF; // 見つからなかった + i = index[crc >> index_shift]; // 配列内のどこにあるか + // リニア・サーチで残りから探す + while (i < block_count){ + if (crcs[i] > crc) + break; // 上回るなら目的の値は存在しない + if (crcs[i] == crc){ + j = i; + break; + } + i++; + } + // 一致するブロックを優先度別に記録する + i1 = i2 = i3 = i4 = -1; + for (; j < block_count; j++){ // 複数ブロックが一致するかもしれないので + if (crcs[j] != crc) + break; // CRC-32 が一致しなくなったら抜ける + i = order[j]; + if (memcmp(hash, s_blk[i].hash, 20) == 0){ // チェックサムが一致するか確かめる + find_flag = 4; + if ((s_blk[i].exist & 0x1000) == 0){ // このファイル内で未発見のブロックを優先的に探す + if (s_blk[i].file == num1){ // 指定されたファイル番号のブロック + i1 = i; + break; // 最優先のブロックを見つけた場合は直ちにループから抜ける + } else { + i2 = i; + } + } else { // このファイル内で発見済みのブロック + if (s_blk[i].file == num1){ + i3 = i; + } else { + i4 = i; + } + } + } + } + // 最も優先度の高いブロックとして認識する (前方からの時とは順序が異なる) + if (i1 >= 0){ + i = i1; // 指定されたファイル番号で未発見のブロック + } else if (i3 >= 0){ + i = i3; // 指定されたファイル番号で発見済みのブロック + } else if (i2 >= 0){ + i = i2; // 他のファイル番号で未発見のブロック + } else if (i4 >= 0){ + i = i4; // 他のファイル番号で発見済みのブロック + } + } + } + if (find_flag >= 0){ // その位置でブロックのチェックサムが一致すれば + num = s_blk[i].file; // 見つけたブロックが属するファイル番号 + if (s_blk[i].exist == 0){ // 未発見のブロックなら + if (switch_v & 16){ // コピーする + if (open_temp_file(temp_path, num, files, sc)) + return -1; + if (file_copy_data(hFile, file_off, sc->hFile_tmp, + (__int64)(i - files[num].b_off) * (__int64)block_size, block_size)){ + printf("file_copy_data, %d\n", i); + return -1; + } + } + s_blk[i].exist = 2; + first_num++; + } + if ((s_blk[i].exist & 0x1000) == 0){ // 同一ファイルで重複していても一回だけ記録する + find_num++; + write_ini_verify(i, 0, file_off); + } + s_blk[i].exist |= 0x1000; // このファイル内で見つけた印 +/* if (find_flag == 3){ + printf("%9I64d : RN %d, F %d\n", file_off, i, s_blk[i].file); // Next + } else { + printf("%9I64d : RS %d, F %d\n", file_off, i, s_blk[i].file); // Search + }*/ + // 次に見つかるブロックを予測する + find_next = i - 1; + if (find_next < 0){ // 最初までいった + find_next = -1; + } else if (s_blk[find_next].file != num){ // 異なるファイルは連続しない + find_next = -1; + } + miss_num = 0; + } else { + miss_num++; + //printf("miss = %d / %d\n", miss_num, miss_max); + if (miss_num >= miss_max) + break; + } + file_off -= block_size; + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress_file((int)(((file_size - file_off) * 1000) / (file_size - last_off)), first_num, file_name)) + return -2; + time_last = GetTickCount(); + } + } + } + + return find_num; +} + +#define FAIL_MAX 8 +#define FAIL_LIMIT 10 +#define FAIL_TIME 128 + +// スライド検査でソース・ブロックを探す (サブ・スレッドがブロックを読み込む) +// ブロック・サイズ * 3 < 4GB であること +// -2=キャンセル, -1=エラー, 0=見つからない, 1~=見つかった個数 +static int search_block_slide( + wchar_t *temp_path, // 作業用、基準ディレクトリが入ってる + int num1, // file_ctx における初期状態のファイル番号 (未知なら -1) + unsigned int last_size, // 末尾ブロックのサイズ + wchar_t *file_name, // 表示するファイル名 + HANDLE hFile, // ファイルのハンドル + __int64 file_size, // 存在するファイルのサイズ + __int64 last_off, // どこから検査を開始するか (ここまでは既に検査済み) + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc) +{ + unsigned char *buf, hash[16], hash2[16], err_mag, *short_use; + int i, j, find_num, find_flag, find_next, find_last, short_next; + int block_count, short_count, tiny_count, tiny_skip, num, i1, i2, i3, i4; + int *order, *index, index_shift; + unsigned int len, off, end_off, err_off; + unsigned int prev_crc, fail_count, rear_off, overlap_count; + unsigned int crc, *crcs, *short_crcs; + unsigned int time_last, time_slide; + __int64 file_off, file_next, short_off, fail_off; + + if (file_size + 1 < last_off + (__int64)(sc->min_size)) + return 0; // 小さすぎるファイルは調べない + find_num = 0; // このファイル内で何ブロック見つけたか + find_next = -1; // 次に見つかると予想したブロックの番号 + find_last = -1; // 最後に見つけたブロックの番号 (-1=不明) + short_next = -1; + fail_count = 0; // CRC は一致したけど MD5 が違った回数 + fail_off = 0; + rear_off = 0; + if (num1 >= 0){ // ファイル番号が指定されてるなら + find_next = (int)(last_off / (__int64)block_size); // 検査開始位置のブロック番号 + find_next += files[num1].b_off; + if (last_off > 0) // 完全なブロックが何個か見つかってるなら + find_last = find_next - 1; // 最後に見つけたブロックの番号 + if ((last_off >= files[num1].size) || (last_off + block_size > file_size + 1)) + find_next = -1; // 予想位置がファイル・サイズを超えると駄目 + if ((last_size < block_size) && (files[num1].b_num >= 2) && // 末尾の半端なブロックの番号と想定位置 + (last_off < files[num1].size) && (files[num1].size <= file_size + 1)){ + short_next = files[num1].b_off + files[num1].b_num - 1; // 末尾ブロックの番号 + if (find_next == short_next) + find_next = -1; // 予想が重複したら末尾ブロックとして探す + short_off = files[num1].size - last_size; + // ファイルサイズが1ブロック未満でも、同じサイズならエラー訂正を試みる + } else if ((last_off == 0) && (file_size == files[num1].size) && (file_size < (__int64)block_size)){ + short_off = 0; + short_next = files[num1].b_off; + } + if (file_size > files[num1].size){ + rear_off = (unsigned int)((file_size - files[num1].size) % (__int64)block_size); + } else if (file_size < files[num1].size){ + rear_off = block_size - (unsigned int)((files[num1].size - file_size) % (__int64)block_size); + } + //printf("file = %d, find_next = %d, find_last = %d\n", num1, find_next, find_last); + //printf("short_off = %I64d, short_next = %d, rear_off = %d\n", short_off, short_next, rear_off); + } + file_off = last_off; // 検査開始位置から調べる + buf = sc->buf; + // インデックス・サーチ用 + block_count = sc->block_count; + order = sc->order; + crcs = order + block_count; + index = crcs + block_count; + index_shift = sc->index_shift; + // 半端なブロックのパディング部分を取り除いた CRC-32 + short_count = sc->short_count; + short_crcs = index + (unsigned int)(1 << (32 - index_shift)); + short_use = (unsigned char *)(short_crcs + entity_num); + tiny_count = 0; + tiny_skip = 0; + + // 最初はブロック・サイズの倍まで読み込む + if (file_size < file_off + ((__int64)block_size * 2)){ + len = (unsigned int)(file_size - file_off); + memset(buf + len, 0, (block_size * 2) - len); + } else { + len = block_size * 2; + } + if (file_read_data(hFile, file_off, buf, len)) + return 0; // エラーなら検査を終了する + if (file_size <= file_off + (__int64)block_size){ // 検査範囲がブロック・サイズ以下なら + file_next = file_size + 1; // 最初のループで検査を終える + } else { + file_next = file_off + len; // ファイル・ポインターの位置 + } + sc->hFile = hFile; + time_last = GetTickCount(); + if (short_count > 0){ // 小さなファイルの個数 + for (num = 0; num < entity_num; num++){ + if (short_use[num] & 1) + tiny_count++; + } + } + // 小さなファイルの半端なブロックの検査 + if ((short_count > 0) && (last_off == 0)){ + //printf("tiny search from %I64d, size = %d, count = %d, %d\n", file_off, len, short_count, tiny_count); + for (num = 0; num < entity_num; num++){ // ファイル番号 + if (short_use[num] == 0) + continue; // 半端なブロックを含まないファイルは除外する + i = files[num].b_off + files[num].b_num - 1; // 末尾ブロックの番号 + last_size = s_blk[i].size; + // 未発見で、検査データより小さな、半端な末尾ブロックなら + if ((last_size <= len) && ((s_blk[i].exist & 0x1000) == 0)){ + if ((short_use[num] & 4) == 0){ // パディング部分を取り除いた CRC-32 を逆算する + short_crcs[num] = crc_reverse_zero(s_blk[i].crc, block_size - last_size); + short_use[num] |= 4; // CRC-32 計算済みの印 + } + //printf("tiny %d, size = %d, CRC = 0x%08X, 0x%08X\n", num, last_size, crc, short_crcs[num]); + if (crc_update(0, buf, last_size) == short_crcs[num]){ // CRC-32 が一致した + data_md5_block(buf, last_size, hash); + if (memcmp(hash, s_blk[i].hash, 16) == 0){ // 更に MD5 も一致すれば + if (s_blk[i].exist == 0){ // 未発見のブロックなら + if (switch_v & 16){ // コピーする + if (open_temp_file(temp_path, num, files, sc)) + return -1; + if (file_write_data(sc->hFile_tmp, + (__int64)(i - files[num].b_off) * (__int64)block_size, buf, last_size)){ + printf("file_write_data, %d\n", i); + return -1; + } + } + s_blk[i].exist = 2; + first_num++; + } + if ((s_blk[i].exist & 0x1000) == 0){ // 同一ファイルで重複していても一回だけ記録する + find_num++; + write_ini_verify(i, 0, file_off); + } + s_blk[i].exist |= 0x1000; // このファイル内で見つけた印 + //printf("%9I64d,%8d : T %d, F %d\n", file_off, last_size, i, s_blk[i].file); // Tiny + if (last_off < file_off + last_size) + last_off = file_off + last_size; // 一番大きな半端なブロックの終端 + find_next = -2; // 小さなファイルが見つかった = ブロック検出の予想が外れた + if (i == short_next) + short_next = -1; // 末尾ブロックは検出済み + + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress_file(0, first_num, file_name)) + return -2; + time_last = GetTickCount(); + } + } // 他のより大きなファイルとも一致してるかもしれないので続行する + } + } + } + if (find_next != -2){ // 小さなファイルが見つからなかったなら + tiny_skip = -1; + if ((*((unsigned short *)buf) == 0x7A37) && (*((unsigned int *)(buf + 2)) == 0x1C27AFBC)){ // 7-Zip書庫の SignatureHeader + tiny_skip = 32; + //printf("tiny_skip = 32 (7-Zip header size)\n"); + } else { + i1 = 0; + while (*((unsigned int *)(buf + i1)) == 0x04034b50){ // ZIP書庫の local file header + i2 = *((unsigned int *)(buf + i1 + 18)); // compressed size + i3 = *((unsigned int *)(buf + i1 + 22)); // uncompressed size + if (i2 != i3) + break; // 圧縮されてる + i3 = *((unsigned short *)(buf + i1 + 26)); // file name length + i4 = *((unsigned short *)(buf + i1 + 28)); // extra field length + if ((__int64)i1 + 30 + i3 + i4 + i2 > file_size) + break; // ファイルが途中まで + i1 += 30 + i3 + i4; + if (i2 > 0){ // 空のファイル(サブ・ディレクトリ)は無視する + tiny_skip = i1; + //printf("tiny_skip = %d (ZIP header size + %d + %d)\n", tiny_skip, i3, i4); + break; + } + } + } + } + } + + // ブロック・サイズごとに探す + if (((block_count > 0) && ((file_off + (__int64)block_size <= file_size) + || (find_next >= 0))) || (short_next >= 0)){ // ブロックの位置を予想して探す + // 前からスライドさせながらチェックサムを比較する + //printf("slide search from %I64d, file %d, next = %d\n", file_off, num1, find_next); + off = 0; // buf 内でのオフセット + if (file_size >= (__int64)block_size){ + end_off = block_size; + } else { + end_off = (unsigned int)file_size; + } + crc = crc_update(0, buf, block_size); + while (1){ + //printf("Loop from %9I64d+%8d for %d\n", file_off, off, end_off); + if (file_next < file_size){ // ファイルの終端に達していなければ、残りを読み込む + if ((file_next + (__int64)block_size) > file_size){ + len = (unsigned int)(file_size - file_next); + } else { + len = block_size; + } + sc->size = len; + //_mm_sfence(); + SetEvent(sc->run); // サブ・スレッドに読み込みを開始させる + //printf("read from %I64d, %d byte\n", file_next, len); + } + + time_slide = GetTickCount(); // スライド検査の開始時刻を記録しておく + overlap_count = 0; // ブロック・サイズをスライドする間に何回見つけたか + while (off < end_off){ + find_flag = -2; + // 次の番号のブロックがその位置にあるかを先に調べる (発見済みでも) + if ((short_next >= 0) && (file_off + off == short_off)){ // 半端なブロックなら + i = short_next; + num = s_blk[i].file; + if ((short_use[num] & 4) == 0){ // パディング部分を取り除いた CRC-32 を逆算する + short_crcs[num] = crc_reverse_zero(s_blk[i].crc, block_size - s_blk[i].size); + short_use[num] |= 4; // CRC-32 計算済みの印 + } + //printf("short[%d], size = %d, short_crc = 0x%08X\n", i, s_blk[i].size, short_crcs[num]); + find_flag = correct_error(buf + off, s_blk[i].size, s_blk[i].hash, short_crcs[num], &err_off, &err_mag); + if (find_flag == 0) + find_flag = 2; + } else if ((find_next >= 0) && (file_off + off == last_off)){ // フルサイズのブロックなら + i = find_next; + if (crc == s_blk[i].crc){ + data_md5(buf + off, block_size, hash); + find_flag = -3; // MD5 計算済みの印 + if (memcmp(hash, s_blk[i].hash, 16) == 0) // 更に MD5 も一致すれば + find_flag = 3; + } else { // 相対位置が順当な時だけエラー訂正を試みる + find_flag = correct_error2(buf + off, block_size, crc, s_blk[i].hash, s_blk[i].crc, &err_off, &err_mag); + } + } + if (find_flag < 0){ // 予想したブロックが見つからなかった場合は全てのブロックと比較する +// j = binary_search(order, block_count, crc, s_blk); +// j = index_search(crcs, block_count, crc, index, index_shift); + j = 0x00FFFFFF; // 見つからなかった + i = index[crc >> index_shift]; // 配列内のどこにあるか + // リニア・サーチで残りから探す + while (i < block_count){ + if (crcs[i] > crc) + break; // 上回るなら目的の値は存在しない + if (crcs[i] == crc){ + j = i; + break; + } + i++; + } + // 一致するブロックを優先度別に記録する + i1 = i2 = i3 = i4 = -1; + for (; j < block_count; j++){ // 複数ブロックが一致するかもしれないので + if (crcs[j] != crc) + break; // CRC-32 が一致しなくなったら抜ける + i = order[j]; + if (find_flag != -3){ // CRC-32 が一致したら MD5 を計算する + data_md5(buf + off, block_size, hash); + find_flag = -3; // MD5 計算済みの印 + } + if (memcmp(hash, s_blk[i].hash, 16) == 0){ // MD5 も一致するか確かめる + if ((s_blk[i].exist & 0x1000) == 0){ // このファイル内で未発見のブロックを優先的に探す (内容が重複してる場合に順序を保つ為) + if (s_blk[i].file == num1){ // 指定されたファイル番号のブロック + i1 = i; + break; // 最優先のブロックを見つけた場合は直ちにループから抜ける + } else { + i2 = i; + if (num1 < 0) + break; + } + } else { // このファイル内で発見済みのブロックも比較する (内容をコピーして追加した改変を認識する) + // 半端なブロックはスライド検査で見つけられないので、相対位置を参考にするため + if (s_blk[i].file == num1){ + i3 = i; + } else { + i4 = i; + } + } + } else { // CRC-32 は一致しても MD5 が違うことは、偶然にも発生する + find_flag = -3; // そこで、発生間隔の平均でフリーズしそうか判定する + } + } + // 最も優先度の高いブロックとして認識する + if (i1 >= 0){ + find_flag = 4; + i = i1; // 指定されたファイル番号で未発見のブロック + } else if (i2 >= 0){ + find_flag = 4; + i = i2; // 他のファイル番号で未発見のブロック + } else if (i3 >= 0){ + find_flag = 4; + i = i3; // 指定されたファイル番号で発見済みのブロック + } else if (i4 >= 0){ + find_flag = 4; + i = i4; // 他のファイル番号で発見済みのブロック + } + } + // ブロックが見つからなかった場合は、前回検出ブロックの後だけ、小さなファイルと比較する + // 比較対象が多すぎると遅くなるので、狭い範囲で見つからなければあきらめる + if ((find_flag < 0) && (tiny_count > 0) && (file_off + off == last_off + tiny_skip)){ + //printf("tiny search at %d\n", off); + i1 = -1; + i2 = 0; + for (num = 0; num < entity_num; num++){ // ファイル番号 + if ((short_use[num] & 3) != 1) + continue; // 小さなファイルだけ比較する + i = files[num].b_off; // 小さなファイルの先頭ブロックの番号 + if ((s_blk[i].exist & 0x1000) != 0) + continue; // 同じファイル内で検出済みなら除外する + last_size = s_blk[i].size; + if ((short_use[num] & 4) == 0){ // パディング部分を取り除いた CRC-32 を逆算する + short_crcs[num] = crc_reverse_zero(s_blk[i].crc, block_size - last_size); + short_use[num] |= 4; // CRC-32 計算済みの印 + } + if (crc_update(0, buf + off, last_size) == short_crcs[num]){ // CRC-32 が一致した + data_md5_block(buf + off, last_size, hash); + if (memcmp(hash, s_blk[i].hash, 16) == 0){ // 更に MD5 も一致すれば + // 複数のスライスと一致した場合は大きい方を採用する + if (last_size > (unsigned int)i2){ + i1 = i; + i2 = last_size; + } + } else { + find_flag = -3; + } + } + } + if (i1 >= 0){ // 小さなファイルが見つかった + find_flag = 4; + i = i1; + } else if (tiny_skip == 0){ // ブロック直後なら + i1 = 0; + while (*((unsigned int *)(buf + off + i1)) == 0x04034b50){ // ZIP書庫の local file header + i2 = *((unsigned int *)(buf + off + i1 + 18)); // compressed size + i3 = *((unsigned int *)(buf + off + i1 + 22)); // uncompressed size + if (i2 != i3) + break; // 圧縮されてる + i3 = *((unsigned short *)(buf + off + i1 + 26)); // file name length + i4 = *((unsigned short *)(buf + off + i1 + 28)); // extra field length + if (last_off + i1 + 30 + i3 + i4 + i2 > file_size) + break; // ファイルが途中まで + i1 += 30 + i3 + i4; + if (i2 > 0){ // 空のファイル(サブ・ディレクトリ)は無視する + tiny_skip = i1; + //printf("tiny_skip = %d (header size + %d + %d)\n", tiny_skip, i3, i4); + break; + } + } + } else { + tiny_skip = 0; + } + } + if (find_flag >= 0){ // その位置でブロックのチェックサムが一致すれば + overlap_count++; + num = s_blk[i].file; // 見つけたブロックが属するファイル番号 + // ファイルが途中から始まっているなら、ブロックが分割されてると仮定する + // 検査結果の記録を読み取りやすいように、本来の検出ブロックより先に記録する + if ((sc->flake_count >= 0) && (file_off == 0) && (off > 1)){ // 分割断片は 2バイト以上必要 + i1 = i - 1; // 一個前のブロックの番号 + if (((i1 == files[num].b_off) && (off + 1 == block_size)) // ブロック・サイズ-1 なら訂正できる + || (i1 > files[num].b_off)){ // 先頭ブロックは除外する + // そのファイルで最初にブロックを見つけた時なので、そのファイル内では未発見 + //printf("slice[%3d] : rear flake size = %d \n", i1, off); + find_num++; + write_ini_verify(i1, 8, off); // 断片のサイズ + if (s_blk[i1].exist == 0) // 未発見なら後半の断片を保管する + save_flake(buf, i1, off, 0, sc); + } + } + if (s_blk[i].exist == 0){ // 未発見のブロックなら + if (switch_v & 16){ // コピーする + if (open_temp_file(temp_path, num, files, sc)) + return -1; + if (file_write_data(sc->hFile_tmp, + (__int64)(i - files[num].b_off) * (__int64)block_size, buf + off, s_blk[i].size)){ + printf("file_write_data, %d\n", i); + return -1; + } + } + s_blk[i].exist = 2; + first_num++; + } + if ((s_blk[i].exist & 0x1000) == 0){ // 同一ファイルで重複していても一回だけ記録する + find_num++; + write_ini_verify(i, find_flag, file_off + off); + } + if (find_flag == 1) // 訂正したエラーを戻しておく + buf[off + err_off] ^= err_mag; + if (find_flag > 3){ // スライド検査で見つけた場合 + if (file_off + off + 1 < last_off){ // 前回に見つけたブロックと今回のが重なってるなら + if (last_off - block_size < file_off) + overlap_count++; + if ((overlap_count >= FAIL_MAX) && (overlap_count >= (off >> FAIL_LIMIT))){ // 検出数が多すぎる場合だけ + find_flag = 5; // 重なってない所から次のブロックを探す + err_off = (unsigned int)(last_off - file_off); // 2バイト以上ずれる + if (err_off > block_size) + err_off = block_size; // 最大でもブロック・サイズまで + //printf("cover [%I64d to %I64d], last [%I64d to %I64d], next off = %d, match = %d\n", + // file_off + off, file_off + off + block_size, last_off - block_size, last_off, err_off, overlap_count); + } + } else if ((find_last < i) && (find_last >= files[num].b_off)){ + // 前回見つけたブロックが同じファイルに所属していて + if (file_off + off == last_off + ((__int64)block_size * (__int64)(i - find_last - 1))) + find_flag = 3; // 今回見つけたブロックの位置がずれてなければ + } + } else if (s_blk[i].exist & 0x1000){ + // 順当な位置で見つけた場合でも、同じファイル内で検出済みだったのなら + find_flag = 4; // 破損してる可能性がある + } + s_blk[i].exist |= 0x1000; // このファイル内で見つけた印 + last_off = file_off + off + s_blk[i].size; // 最後に見つけたブロックの終端 + find_last = i; +/* if (find_flag == 1){ + printf("%9I64d+%8u : C %d, F %d\n", file_off, off, i, s_blk[i].file); // Corrected + } else if (find_flag == 2){ + printf("%9I64d+%8u : L %d, F %d\n", file_off, off, i, s_blk[i].file); // Last + } else if (find_flag == 3){ + printf("%9I64d+%8u : N %d, F %d\n", file_off, off, i, s_blk[i].file); // Next + } else if (find_flag == 5){ + printf("%9I64d+%8u : O %d, F %d\n", file_off, off, i, s_blk[i].file); // Overlap + } else if (s_blk[i].size < block_size){ + printf("%9I64d+%8u : T %d, F %d, Size %d\n", file_off, off, i, s_blk[i].file, s_blk[i].size); // Tiny + } else { + printf("%9I64d+%8u : S %d, F %d\n", file_off, off, i, s_blk[i].file); // Search + }*/ + // 次に見つかるブロックを予測する + find_next = i + 1; + if ((find_next >= source_num) || (s_blk[find_next].file != num)){ + // 最後までいった、またはファイルが異なる + short_next = -1; + find_next = -1; + } else if (s_blk[find_next].size < block_size){ // 半端なブロックは別に調べる + short_next = find_next; + short_off = file_off + off + block_size; + //printf("short_off = %I64d, short_next = %d, file = %d\n", short_off, short_next, num); + find_next = -1; + } else { + short_next = files[num].b_off + files[num].b_num - 1; // 末尾ブロックの番号 + if (s_blk[short_next].size < block_size){ // 半端なブロックは別に調べる + short_off = file_off + off + (__int64)(short_next - i) * (__int64)block_size; + //printf("short_off = %I64d, short_next = %d, file = %d\n", short_off, short_next, num); + } else { + short_next = -1; + } + } + tiny_skip = 0; // 小さなファイルをブロック直後に一回だけ探す + // ファイルが途中で終わっているなら、ブロックが分割されてると仮定する + if ((sc->flake_count >= 0) && + (file_off + off + s_blk[i].size + block_size - 1 > file_size) && + (file_off + off + s_blk[i].size + 1 < file_size)){ // 分割断片は 2~ブロック・サイズ-2 バイト + i1 = i + 1; // 一個後ろブロックの番号 (ファイルの末尾ブロックは除く) + i3 = files[num].b_off + files[num].b_num - 1; // そのファイルの末尾のブロック番号 + //printf("id = %d, start = %d, max = %d \n", i1, files[num].b_off, i3); + if ((i1 < i3) && ((s_blk[i1].exist & 0x1000) == 0)){ // ファイル内で未発見なら + i2 = (int)(file_size - file_off - off - s_blk[i].size); // 断片のサイズ + //printf("slice[%3d] : front flake size = %d \n", i1, i2); + find_num++; + write_ini_verify(i1, 9, file_off + off + s_blk[i].size); // 断片の位置 + if (s_blk[i1].exist == 0) // 未発見なら後半の断片を保管する + save_flake(buf + (off + s_blk[i].size), i1, i2, 1, sc); + } + } + if (find_flag <= 3){ // 見つけたのが順当なブロックなら + // 次のブロックは今のブロックの後ろに続くと予想する + off += s_blk[i].size; + if (off <= end_off) + crc = crc_update(0, buf + off, block_size); + time_slide = GetTickCount(); // スライド検査の開始時刻を更新する + } else { // ブロック番号が順番通りになってないなら (破損してる個所) + // ファイル内容に重複があると、後のブロックを先に見つけてしまって、 + // ブロック・サイズ分ずらすと本来のブロックを見つけれなくなるので 1バイトずらす + prev_crc = crc; + crc = CRC_SLIDE_CHAR(crc, buf[off + block_size], buf[off]); + off++; + if (crc == prev_crc){ // 同じ CRC のブロックを重なった範囲内で発見しないようにする + data_md5(buf + off, block_size, hash2); + if (memcmp(hash, hash2, 16) == 0){ // 1バイトずらした後でも MD5 が同じなら、同じデータが連続してる + //int off2 = off - 1; + while ((off < end_off) && (file_off + off < last_off) && (crc == prev_crc)){ + crc = CRC_SLIDE_CHAR(crc, buf[off + block_size], buf[off]); + off++; + } + // printf("Because data is same, skip %d after find.\n", off - off2); + //} else { + // printf("Though CRC is same after find, MD5 is different.\n"); + } + } + if ((find_flag == 5) && (off < err_off)){ + off = err_off; // 前回発見したブロックの終了位置 + if (off <= end_off) + crc = crc_update(0, buf + off, block_size); + //printf("off = %d after cover, end_off = %d\n", off, end_off); + } + } + fail_count = 0; // 誤検出回数を初期化する + fail_off = file_off + off; // スライド検査の開始位置 + } else { // 発見できなかったら、検査位置を 1バイトずらす + prev_crc = crc; + crc = CRC_SLIDE_CHAR(crc, buf[off + block_size], buf[off]); + off++; + if (crc == prev_crc){ // 1バイトずらしても同じ CRC なら、データが同じ区間は無視する + if (find_flag != -3) + data_md5(buf + off - 1, block_size, hash); + data_md5(buf + off, block_size, hash2); + if (memcmp(hash, hash2, 16) == 0){ // 1バイトずらした後でも MD5 が同じなら、同じデータが連続してる + //int off2 = off - 1; + while ((off < end_off) && (crc == prev_crc)){ + crc = CRC_SLIDE_CHAR(crc, buf[off + block_size], buf[off]); + off++; + } + find_flag = -4; // CRC と MD5 の両方が同じだった + //printf("Because data is same, skip %d.\n", off - off2); + } else { + find_flag = -3; // 後で MD5 が異なる回数を数える + //printf("Though CRC is same, MD5 is different.\n"); + } + } + if (find_flag == -3){ // MD5 が異なる回数を数える + fail_count++; + //printf("MD5 fail %u, off = %I64d, Ave = %u\n", fail_count, file_off + off, (unsigned int)((file_off + off - fail_off) / (__int64)fail_count)); + if (fail_count == FAIL_MAX){ + // CRC が同じで MD5 が異なることが多すぎ (直近 8回の発生間隔が平均 1KB 以下) なら + if (file_off + off - fail_off <= (__int64)(FAIL_MAX << FAIL_LIMIT)){ + // 検査が遅くなってる (スライドし始めて 128 ms 以上経過) なら + if (GetTickCount() - time_slide >= FAIL_TIME){ + if (off <= rear_off){ // 一時的に簡易検査の前方一致・後方一致に切り替える + if (off < rear_off){ + off = rear_off; + crc = crc_update(0, buf + off, block_size); + } + //printf("skip to rear %d, off = %I64d\n", off, file_off + off); + } else if (off < end_off){ + off = end_off; + if (off == block_size) // 次のループの分を計算しておく + crc = crc_update(0, buf + off, block_size); + //printf("skip to block, off = %I64d\n", file_off + off); + } + } + } + fail_count = 0; // 回数を数えなおす + fail_off = file_off + off; + } + } + } + } + + if (file_next > file_size) + break; // バッファーの終端でもうファイルの残りが無いなら終わる + // ブロックの終端に達したのならバッファー内容をずらす + //printf("%9I64d, off = %d, next = %d\n", file_off, off, find_next); + memcpy(buf, buf + block_size, block_size); // 二個目を先頭に移す + off -= block_size; + if (file_next < file_size){ // ファイルの終端に達していなければ、残りを読み込む +/* if ((file_next + (__int64)block_size) > file_size){ + len = (unsigned int)(file_size - file_next); + memset(buf + len, 0, block_size - len); + } else { + len = block_size; + } + if (!ReadFile(hFile, buf + block_size, len, &i, NULL) || (len != (unsigned int)i)) + memset(buf + block_size, 0, len); // 読み込み時にエラーが発生した部分は 0 にしておく +*/ + WaitForSingleObject(sc->end, INFINITE); // サブ・スレッドの読み込み終了の合図を待つ + if (sc->size == 0xFFFFFFFF) + return find_num; // 読みこみ時にエラーが発生したら、検査を終了する + memcpy(buf + block_size, buf + (block_size * 2), len); // 読み込んでおいた分を移す + memset(buf + (block_size + len), 0, block_size - len); + file_next += len; // ファイル・ポインターの位置 + } else if (file_next == file_size){ // 既に読み込みが終わってるなら + end_off = (unsigned int)(file_size - file_off - (__int64)block_size); // 最後のループだけ検査範囲が狭い + //printf("last loop: off = %u, end_off = %u\n", off, end_off); + if (off >= end_off) + break; // 検査する所が残ってない + memset(buf + end_off, 0, (block_size * 2) - end_off); + file_next++; // 次にループしないようにしておく + } + if (off > 0) // CRCを計算しなおしておく + crc = crc_update(0, buf + off, block_size); + file_off += block_size; + + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress_file((int)((file_off * 1000) / file_size), first_num, file_name)) + return -2; + time_last = GetTickCount(); + } + } + } + + // 小さなファイルまたは末尾の半端なブロックの検査 + if ((short_count > 0) && (last_off + (__int64)(sc->min_size) <= file_size)){ + if ((file_off == 0) && (file_size <= (__int64)block_size * 2)){ // 先頭部分は既に読み込んでる + len = (unsigned int)file_size; + i2 = 0; + } else { + len = block_size * 2 - 1; // 最大でブロック・サイズ * 2 - 1 バイトを読み込む + if ((__int64)len > file_size) + len = (unsigned int)file_size; + file_off = file_size - len; + //printf("last_off = %I64d, min size = %d\n", last_off, sc->min_size); + if (file_off < last_off){ + file_off = last_off; + len = (unsigned int)(file_size - file_off); + } + i2 = -1; // まだファイルからデータを読み込んでない印 + } + //printf("last search from %I64d, size = %d\n", file_off, len); + for (num = 0; num < entity_num; num++){ // ファイル番号 + if (short_use[num] == 0) + continue; // 半端なブロックを含まないファイルは除外する + i = files[num].b_off + files[num].b_num - 1; // 末尾ブロックの番号 + last_size = s_blk[i].size; + // 未発見で、検査データより小さな、半端な末尾ブロックなら + if ((last_size <= len) && ((s_blk[i].exist & 0x1000) == 0)){ + if (i2 < 0){ // 必要になって初めて読み込む + i2 = 0; + if (file_read_data(hFile, file_off, buf, len)) + return find_num; // エラーなら検査を終了する + //buf[len] = 0; // ずらす分だけ末尾を 0 で埋めておく + } + if ((short_use[num] & 4) == 0){ // パディング部分を取り除いた CRC-32 を逆算する + short_crcs[num] = crc_reverse_zero(s_blk[i].crc, block_size - last_size); + short_use[num] |= 4; // CRC-32 計算済みの印 + } + off = len - last_size; + if (crc_update(0, buf + off, last_size) == short_crcs[num]){ // CRC-32 が一致した + data_md5_block(buf + off, last_size, hash); + if (memcmp(hash, s_blk[i].hash, 16) == 0){ // 更に MD5 も一致すれば + // 末尾ブロックがファイルの途中にあれば、先のブロックが分割されてると仮定する + if ((sc->flake_count >= 0) && (file_off == 0) && (off > 1) && (off < block_size)){ + i1 = i - 1; // 一個前のブロックの番号 + if (((i1 == files[num].b_off) && (off + 1 == block_size)) // ブロック・サイズ-1 なら訂正できる + || (i1 > files[num].b_off)){ // 先頭ブロックは除外する + // そのファイルで最初にブロックを見つけた時なので、そのファイル内では未発見 + //printf("slice[%3d] : rear flake size = %d \n", i1, off); + find_num++; + write_ini_verify(i1, 8, off); // 断片のサイズ + if (s_blk[i1].exist == 0) // 未発見なら後半の断片を保管する + save_flake(buf, i1, off, 0, sc); + } + } + if (s_blk[i].exist == 0){ // 未発見のブロックなら + if (switch_v & 16){ // コピーする + if (open_temp_file(temp_path, num, files, sc)) + return -1; + if (file_write_data(sc->hFile_tmp, + (__int64)(i - files[num].b_off) * (__int64)block_size, buf + off, last_size)){ + printf("file_write_data, %d\n", i); + return -1; + } + } + s_blk[i].exist = 2; + first_num++; + } + if ((s_blk[i].exist & 0x1000) == 0){ // 同一ファイルで重複していても一回だけ記録する + find_num++; + write_ini_verify(i, 0, file_off + off); + } + s_blk[i].exist |= 0x1000; // このファイル内で見つけた印 + //printf("%9I64d,%8d : E %d, F %d\n", file_off + off, last_size, i, s_blk[i].file); // End + + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress_file(1000, first_num, file_name)) + return -2; + time_last = GetTickCount(); + } + } // 他のより大きな末尾ブロックとも一致してるかもしれないので続行する + } + } + } + } + + return find_num; +} + +// 整列順にソース・ブロックのチェックサムを調べる(位相のずれに対処しない) +// -2=キャンセル, -1=エラー, 0=見つからない, 1~=見つかった個数 +static int search_block_align( + int num1, // file_ctx におけるファイル番号 (未知なら -1) + unsigned int last_size, // 末尾ブロックのサイズ + wchar_t *file_name, // 表示するファイル名 + HANDLE hFile, // ファイルのハンドル + __int64 file_size, // 存在するファイルのサイズ + __int64 file_off, // どこから検査を開始するか (ここまでは既に検査済み) + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + unsigned char hash[20]; + int i, b_last, find_num, find_next; + unsigned int time_last; + + if (num1 < 0) // ファイル番号が指定されてないと検査できない + return 0; + if (file_size < file_off + last_size) + return 0; // 小さすぎるファイルは調べない + find_num = 0; // このファイル内で何ブロック見つけたか + b_last = files[num1].b_off + files[num1].b_num - 1; // 末尾ブロックの番号 + find_next = (int)(file_off / (__int64)block_size); // 検査開始位置のブロック番号 + find_next += files[num1].b_off; + time_last = GetTickCount(); + + // 前から順にブロック・サイズごとにチェックサムを比較する + //printf("search from %I64d, file %d, from %d to %d\n", file_off, num1, find_next, b_last); + // 本来のファイルサイズまでしか調べない + while ((file_off + (__int64)block_size <= file_size) && (file_off + (__int64)block_size <= files[num1].size)){ + // 次の番号のブロックがその位置にあるかを先に調べる (発見済みでも) + if (file_md5_crc32_block(hFile, file_off, block_size, hash) != 0) + return -1; // ファイルアクセスのエラーを致命的と見なす + if (memcmp(hash, s_blk[find_next].hash, 20) == 0){ // チェックサムが一致するか確かめる + i = find_next; + // その位置でブロックのチェックサムが一致すれば + if (s_blk[i].exist == 0){ // 未発見のブロックなら + s_blk[i].exist = 2; + first_num++; + } + if ((s_blk[i].exist & 0x1000) == 0){ // 同一ファイルで重複していても一回だけ記録する + find_num++; + write_ini_verify(i, 0, file_off); + } + s_blk[i].exist |= 0x1000; // このファイル内で見つけた印 + //printf("%9I64d : N %d\n", file_off, i); // Next + } + find_next++; + file_off += block_size; + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress_file((int)((file_off * 1000) / file_size), first_num, file_name)) + return -2; + time_last = GetTickCount(); + } + } + + // 末尾の半端なブロックの検査 + if ((last_size < block_size) && (file_size >= files[num1].size)){ + file_off = files[num1].size - last_size; + if (file_md5_crc32_block(hFile, file_off, last_size, hash) != 0){ + return -1; // ファイルアクセスのエラーを致命的と見なす + } else { + i = b_last; + if (memcmp(hash, s_blk[i].hash, 20) == 0){ // チェックサムが一致するか確かめる + if (s_blk[i].exist == 0){ // 未発見のブロックなら + s_blk[i].exist = 2; + first_num++; + } + find_num++; + write_ini_verify(i, 0, file_off); + s_blk[i].exist |= 0x1000; // このファイル内で見つけた印 + //printf("%9I64d : L %d\n", file_off, i); // Last + } + } + } + + return find_num; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ファイルごとに見つけたスライス数を表示する +static int show_file_slice( + char *ascii_buf, + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + int i, id, find_num, find_total; + + find_total = 0; // 利用可能なソース・ブロックの数 + find_num = 0; + id = -1; // ファイル番号 + for (i = 0; i < source_num; i++){ + if (s_blk[i].exist != 0){ + find_total++; + if (s_blk[i].exist & 0x1000){ // 今回の検査で発見した印 + if (s_blk[i].file != id){ + if (id >= 0){ + utf16_to_cp(list_buf + files[id].name, ascii_buf, cp_output); + printf(" = %8d : \"%s\"\n", find_num, ascii_buf); + } + id = s_blk[i].file; // 見つけたスライスが属するファイル番号 + find_num = 0; + } + find_num++; + s_blk[i].exist &= 0x0FFF; // ファイル内で検出の印を消す + } + } + } + if (id >= 0){ + utf16_to_cp(list_buf + files[id].name, ascii_buf, cp_output); + printf(" = %8d : \"%s\"\n", find_num, ascii_buf); + } + + return find_total; +} + +// 破損ファイルの先頭から破損箇所までを作業ファイルにコピーする +static int file_copy_size( + int num1, // ファイル番号 + wchar_t *file_name, // 表示するファイル名 + __int64 file_size, // 経過を比較するファイル・サイズ + __int64 copy_size, // コピーするサイズ + HANDLE hFile, // ファイルのハンドル + HANDLE hFile_tmp) // 作業ファイルのハンドル +{ + unsigned char buf[IO_SIZE]; + unsigned int rv, len; + unsigned int time_last, block_end; + __int64 copy_end; + LARGE_INTEGER qwi; // Quad Word Integer + + // ファイル位置を先頭にする + qwi.QuadPart = 0; + if (!SetFilePointerEx(hFile, qwi, NULL, FILE_BEGIN)){ + print_win32_err(); + return 1; + } + if (!SetFilePointerEx(hFile_tmp, qwi, NULL, FILE_BEGIN)){ + print_win32_err(); + return 1; + } + + // コピーする + time_last = GetTickCount(); + block_end = 0; + copy_end = 0; + while (copy_size > 0){ + len = IO_SIZE; + if (copy_size < IO_SIZE) + len = (unsigned int)copy_size; + copy_size -= len; + block_end += len; + if (!ReadFile(hFile, buf, len, &rv, NULL)){ + print_win32_err(); + return 1; + } + if (len != rv) + return 1; // 指定サイズを読み込めなかったらエラーになる + if (!WriteFile(hFile_tmp, buf, len, &rv, NULL)){ + print_win32_err(); + return 1; + } + + if (block_end >= block_size){ // ブロック・サイズごとに + block_end -= block_size; + copy_end += block_size; + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress_file((int)((copy_end * 1000) / file_size), -1, file_name)) + return 2; + time_last = GetTickCount(); + } + } + } + + return 0; +} + +// ソース・ファイルのスライスを探す (分割ファイルも) +// 0=完了, 1=エラー, 2=キャンセル +int check_file_slice( + char *ascii_buf, // 作業用 + wchar_t *file_path, // 作業用 + int num, // file_ctx におけるファイル番号 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc) // スライス検査用の情報 +{ + wchar_t temp_path[MAX_LEN], *file_name; + int i, find_num, b_last, dir_len, name_off, name_len, num1; + unsigned int last_size, meta_data[7]; + __int64 file_size; // 存在するファイルのサイズは本来のサイズとは異なることもある + HANDLE hFile, hFind; + WIN32_FIND_DATA FindData; + + file_name = list_buf + files[num].name; + wcscpy(temp_path, base_dir); // 基準ディレクトリを入れておく + + // 末尾スライスの大きさ (PAR2 のチェックサムはパディング部分を含む) + last_size = (unsigned int)(files[num].size % (__int64)block_size); + if (last_size == 0) + last_size = block_size; + b_last = files[num].b_off + files[num].b_num; + if (files[num].state & 0x80){ + num1 = -1; // チェックサムが存在しない場合はファイル番号を指定しない + } else { + num1 = num; + } + + if (files[num].state & 0x1A){ // 破損 0x02、追加 0x10、破損して別名 0x28 ならソース・ファイルを検査する + int comp_num; + wcscpy(file_path, base_dir); + wcscpy(file_path + base_len, file_name); + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ // 存在するはずのソース・ファイルにアクセスできない + print_win32_err(); + return 1; // スライス認識や作業ファイルの再設定がめんどうなのでエラーにする + } + prog_last = -1; // 経過表示がまだの印 + find_num = check_ini_verify(file_name, hFile, num1, meta_data, files, s_blk, sc); + // find_num = ファイル内で検出したスライス数 (他のファイルのスライスも含む) + memcpy(&file_size, meta_data, 8); + //printf("ini = %d, file_size = %I64d, first count = %d\n", find_num, file_size, first_num); + if (file_size + 1 < (__int64)(sc->min_size)){ // 小さすぎるファイルは調べない + find_num = 0; + } else if (find_num != -1){ // エラー以外なら + // 破損ファイルの先頭に完全なスライスが存在するか + __int64 file_off = 0; + comp_num = 0; // 先頭の完全なスライスの数 + if (files[num].state & 2){ // 破損なら上位 24-bit に先頭の完全なスライス数が記録されてる + comp_num = (unsigned int)(files[num].state) >> 8; + files[num].state &= 0xFF; // 上位 24-bit の記録を消しておく + if (comp_num > 0){ // 破損ファイルの先頭に完全なスライスが見つかってるなら + for (i = files[num].b_off; i < files[num].b_off + comp_num; i++){ + s_blk[i].exist = 0x1002; // このファイル内で見つけた印 + file_off += block_size; + } + if ((switch_v & (16 | 4)) == 16){ // コピーする + if (open_temp_file(temp_path, num, files, sc)){ + CloseHandle(hFile); + return 1; + } + if (i = file_copy_size(num1, file_name, file_size, file_off, hFile, sc->hFile_tmp)){ + CloseHandle(hFile); + return i; + } + } + } + } else if (files[num].state & 8){ // 破損して別名が見つかっても、破損箇所は検査する + comp_num = (unsigned int)(files[num].state) >> 8; + files[num].state &= 0xFF; // 上位 24-bit の記録を消しておく + if (comp_num > 0){ + for (i = files[num].b_off; i < files[num].b_off + comp_num; i++){ + s_blk[i].exist = 0x1001; // このファイル内で見つけた印 (別名ファイル内では完全) + file_off += block_size; + } + } + } else if (files[num].state & 16){ // 追加なら全てのスライスが完全なはず + comp_num = files[num].b_num; + for (i = files[num].b_off; i < b_last; i++) + s_blk[i].exist = 0x1001; // このファイル内で見つけた印 (本来のサイズまでは完全) + } + if (comp_num == files[num].b_num) + file_off = files[num].size; // 最後まで完全 + //printf("first search = %d, file_off = %I64d, state = 0x%08X\n", comp_num, file_off, files[num].state); + + // 検査結果として記録されるのは file_off 以降で見つかったスライスのみ + if (find_num >= 0){ // 検査結果の記録がある + find_num += comp_num; + } else if (find_num == -3){ // 検査結果の記録が無ければ + //printf("switch_v = %d, file_off = %I64d\n", switch_v, file_off); + // 破損個所以降を調べる + if (switch_v & 4){ // 順列検査 + find_num = search_block_align(num1, last_size, file_name, + hFile, file_size, file_off, files, s_blk); + } else if (switch_v & 1){ // 簡易検査 + find_num = search_block_simple(temp_path, num1, last_size, file_name, + hFile, file_size, file_off, 0, files, s_blk, sc); + } else { // 詳細検査 + find_num = search_block_slide(temp_path, num1, last_size, file_name, + hFile, file_size, file_off, files, s_blk, sc); + } + if (find_num >= 0){ // 検査結果を記録する + write_ini_verify2(num1, meta_data, find_num); + find_num += comp_num; + } else { // エラーが発生したら + write_ini_verify2(num1, meta_data, -1); // 検査中の記録を破棄する + } + } + } + CloseHandle(hFile); // 検査したファイルを閉じる + if (find_num < 0) // エラー + return -find_num; // 検査時にエラーが発生した + if (find_num > 0){ // スライスが検出された時 + if (sc->flake_count > 0){ // スライス断片の一致を調べる + if (check_flake(temp_path, files, s_blk, sc) != 0) + return 1; + } + //printf("find item = %d, first count = %d\n", find_num, first_num); + print_progress_file(-1, first_num, NULL); // 重複を除外した利用可能なソース・ブロック数を表示する + } + + // ソース・ファイルはスライスを検出できなくても検査結果を表示する + utf16_to_cp(file_name, ascii_buf, cp_output); + if (files[num].state & 16){ // 追加なら + printf("%13I64d Appended : \"%s\"\n", file_size, ascii_buf); + } else { // 破損、または破損して別名 + printf("%13I64d Damaged : \"%s\"\n", file_size, ascii_buf); + } + // 指定されたファイルに属するスライスを見つけた数 + comp_num = 0; + for (i = files[num].b_off; i < b_last; i++){ + if (s_blk[i].exist & 0x1000){ + comp_num++; + s_blk[i].exist &= 0x0FFF; // ファイル内で検出の印を消す + } + } + printf(" = %8d : \"%s\"\n", comp_num, ascii_buf); + if (find_num > comp_num) // 他のファイルに属するスライスを見つけた + show_file_slice(ascii_buf, files, s_blk); + fflush(stdout); + find_num = comp_num; + + } else { // 消失なら、他でスライスを見つけてないか確かめる + find_num = 0; + for (i = files[num].b_off; i < b_last; i++){ + if (s_blk[i].exist != 0) + find_num++; + } + } + + // このファイルのスライスが全て検出済みなら、分割ファイルを探さない + if (find_num == files[num].b_num) + return 0; + + // 順列検査では分割ファイルを探さない + if (switch_v & 4) + return 0; + + // 分割ファイルや類似名のファイルを探す + get_base_dir(file_name, file_path); // 記録されてるファイル名のサブ・ディレクトリ + dir_len = base_len + (int)wcslen(file_path); // ディレクトリ部分の長さ + set_splited_filename(file_path, file_name, dir_len); + //printf_cp("search %s\n", file_path); + hFind = FindFirstFile(file_path, &FindData); + if ((hFind == INVALID_HANDLE_VALUE) && (files[num].state == 1)){ // 消失でチェックサムがある + // 元のファイルが消失してるのに、分割ファイルが全く見つからなければ + if (set_similar_filename(file_path, file_name, dir_len) != 0){ // 文字化けファイルを探す + //printf_cp("similar %s\n", file_path); + hFind = FindFirstFile(file_path, &FindData); + } + } + if (hFind == INVALID_HANDLE_VALUE) + return 0; + name_off = (int)(offset_file_name(file_name) - list_buf); // 見つけたファイルとの比較用にファイル名の相対位置 + name_len = (int)wcslen(list_buf + name_off); + do { + if (cancel_progress() != 0){ // キャンセル処理 + FindClose(hFind); + return 2; + } + + //printf_cp("find = %s\n", FindData.cFileName); + // フォルダは無視する + if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) + continue; + // 発見したファイル名が長すぎる場合は無視する + if (dir_len + wcslen(FindData.cFileName) >= MAX_LEN) + continue; + // ファイル・サイズが最小ブロックよりも小さいファイルは無視する + file_size = ((__int64)(FindData.nFileSizeHigh) << 32) | (__int64)(FindData.nFileSizeLow); + if (file_size + 1 < (__int64)(sc->min_size)) + continue; + // ファイル名に付加されてる部分が妥当でないファイルは無視する + if (check_extra_part(FindData.cFileName, list_buf + name_off, name_len)) + continue; + + // 破損してないリカバリ・ファイルは無視する + wcscpy(temp_path + base_len, file_path + base_len); + wcscpy(temp_path + dir_len, FindData.cFileName); // 完全ファイル・パスにする + if (search_file_path(recv_buf, recv_len, temp_path)) + continue; + // ソース・ファイルや検査済みの分割・類似名ファイルは無視する + wcscpy(file_path, temp_path + base_len); // 基準ディレクトリからの相対ファイル・パスにする + if (search_file_path(list_buf + 1, list_len - 1, file_path)) + continue; + // 破損したソース・ファイルの作業ファイルは無視する・・・付加部分をチェックしてるので不要 + //printf_cp("verify = %s\n", file_path); + + // 分割先ファイルを検査する + hFile = CreateFile(temp_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile != INVALID_HANDLE_VALUE){ // ファイルを開けたなら内容を検査する + prog_last = -1; // 経過表示がまだの印 + find_num = check_ini_verify(FindData.cFileName, hFile, num1, meta_data, files, s_blk, sc); + //printf("ini = %d, file_size = %I64d\n", find_num, file_size); + if (find_num == -3){ // 検査結果の記録が無ければ + if (switch_v & 1){ // 簡易検査 + find_num = search_block_simple(temp_path, num1, last_size, FindData.cFileName, + hFile, file_size, 0, 1, files, s_blk, sc); + } else { // 詳細検査 + find_num = search_block_slide(temp_path, num1, last_size, FindData.cFileName, + hFile, file_size, 0, files, s_blk, sc); + } + // 検査結果を記録する + write_ini_verify2(num1, meta_data, find_num); + } + CloseHandle(hFile); // 検査したファイルを閉じる + if (add_file_path(file_path)){ // 検査済みファイルのファイル名を記録する + printf("add_file_path\n"); + find_num = -1; + } + if (find_num < 0){ + FindClose(hFind); + return -find_num; // 検査時にエラーが発生した + } + if (find_num > 0){ // スライスが検出された時 + if (sc->flake_count > 0){ // スライス断片の一致を調べる + if (check_flake(temp_path, files, s_blk, sc) != 0){ + FindClose(hFind); + return 1; + } + } + //printf("find item = %d, first count = %d\n", find_num, first_num); + print_progress_file(-1, first_num, NULL); // 重複を除外した利用可能なソース・ブロック数を表示する + + // 検査したファイル名を表示する (分割先のファイル・サイズにする) + utf16_to_cp(file_path, ascii_buf, cp_output); + printf("%13I64d Found : \"%s\"\n", file_size, ascii_buf); + json_add_found(file_path, 0); + // ファイルごとに見つけたスライス数を表示する + show_file_slice(ascii_buf, files, s_blk); + fflush(stdout); + if (first_num >= source_num){ // 全てのスライスが見つかったら + FindClose(hFind); // その時点で検索を中止する + return 0; + } + } else { + print_progress_done(); // 経過表示があれば 100% にしておく + } + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + + return 0; +} + +// 分割ファイルに含まれる他のファイルのスライスを探す +// 0=完了, 1=エラー, 2=キャンセル +int search_file_split( + char *ascii_buf, // 作業用 + wchar_t *file_path, // 作業用 + int num, // file_ctx におけるファイル番号 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc) // スライス検査用の情報 +{ + wchar_t temp_path[MAX_LEN], *file_name; + int find_num, dir_len, name_off, name_len; + unsigned int meta_data[7]; + __int64 file_size; // 存在するファイルのサイズは本来のサイズとは異なることもある + HANDLE hFile, hFind; + WIN32_FIND_DATA FindData; + + if (files[num].state & 0x03){ // 先に検索済みなら除外する + int i, b_last; + b_last = files[num].b_off + files[num].b_num; + find_num = 0; + for (i = files[num].b_off; i < b_last; i++){ + if (s_blk[i].exist != 0) + find_num++; + } + if (find_num < files[num].b_num) + return 0; // 消失と破損で、このファイルのスライスが不足してる場合は既に検索済み + } + + file_name = list_buf + files[num].name; + wcscpy(temp_path, base_dir); // 基準ディレクトリを入れておく + + // 分割ファイルを探す + get_base_dir(file_name, file_path); // 記録されてるファイル名のサブ・ディレクトリ + dir_len = base_len + (int)wcslen(file_path); // ディレクトリ部分の長さ + set_splited_filename(file_path, file_name, dir_len); + //printf_cp("search %s\n", file_path); + hFind = FindFirstFile(file_path, &FindData); + if (hFind == INVALID_HANDLE_VALUE) + return 0; + name_off = (int)(offset_file_name(file_name) - list_buf); // 見つけたファイルとの比較用にファイル名の相対位置 + name_len = (int)wcslen(list_buf + name_off); + do { + if (cancel_progress() != 0){ // キャンセル処理 + FindClose(hFind); + return 2; + } + + //printf_cp("find = %s\n", FindData.cFileName); + // フォルダは無視する + if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) + continue; + // 発見したファイル名が長すぎる場合は無視する + if (dir_len + wcslen(FindData.cFileName) >= MAX_LEN) + continue; + // ファイル・サイズが最小ブロックよりも小さいファイルは無視する + file_size = ((__int64)(FindData.nFileSizeHigh) << 32) | (__int64)(FindData.nFileSizeLow); + if (file_size < (__int64)(sc->min_size)) + continue; + // ファイル名に付加されてる部分が妥当でないファイルは無視する + if (check_extra_part(FindData.cFileName, list_buf + name_off, name_len)) + continue; + + // 破損してないリカバリ・ファイルは無視する + wcscpy(temp_path + base_len, file_path + base_len); + wcscpy(temp_path + dir_len, FindData.cFileName); // 完全ファイル・パスにする + if (search_file_path(recv_buf, recv_len, temp_path)) + continue; + // ソース・ファイルや検査済みの分割・類似名ファイルは無視する + wcscpy(file_path, temp_path + base_len); // 基準ディレクトリからの相対ファイル・パスにする + if (search_file_path(list_buf + 1, list_len - 1, file_path)) + continue; + // 破損したソース・ファイルの作業ファイルは無視する・・・付加部分をチェックしてるので不要 + //printf_cp("verify = %s\n", file_path); + + // 分割先ファイルを検査する + hFile = CreateFile(temp_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile != INVALID_HANDLE_VALUE){ // ファイルを開けたなら内容を検査する + prog_last = -1; // 経過表示がまだの印 + find_num = check_ini_verify(FindData.cFileName, hFile, -1, meta_data, files, s_blk, sc); + //printf("ini = %d, file_size = %I64d\n", find_num, file_size); + if (find_num == -3){ // 検査結果の記録が無ければ + if (switch_v & 1){ // 簡易検査 + find_num = search_block_simple(temp_path, -1, 0, FindData.cFileName, + hFile, file_size, 0, 1, files, s_blk, sc); + } else { // 詳細検査 + find_num = search_block_slide(temp_path, -1, 0, FindData.cFileName, + hFile, file_size, 0, files, s_blk, sc); + } + // 検査結果を記録する + write_ini_verify2(-1, meta_data, find_num); + } + CloseHandle(hFile); // 検査したファイルを閉じる + if (add_file_path(file_path)){ // 検査済みファイルのファイル名を記録する + printf("add_file_path\n"); + find_num = -1; + } + if (find_num < 0){ + FindClose(hFind); + return -find_num; // 検査時にエラーが発生した + } + if (find_num > 0){ // スライスが検出された時 + if (sc->flake_count > 0){ // スライス断片の一致を調べる + if (check_flake(temp_path, files, s_blk, sc) != 0){ + FindClose(hFind); + return 1; + } + } + print_progress_file(-1, first_num, NULL); // 重複を除外した利用可能なソース・ブロック数を表示する + + // 検査したファイル名を表示する (分割先のファイル・サイズにする) + utf16_to_cp(file_path, ascii_buf, cp_output); + printf("%13I64d Found : \"%s\"\n", file_size, ascii_buf); + json_add_found(file_path, 0); + // ファイルごとに見つけたスライス数を表示する + show_file_slice(ascii_buf, files, s_blk); + fflush(stdout); + if (first_num >= source_num){ // 全てのスライスが見つかったら + FindClose(hFind); // その時点で検索を中止する + return 0; + } + } else { + print_progress_done(); // 経過表示があれば 100% にしておく + } + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 指定された外部ファイルを検査する +// 0=検査完了, -1=エラー, -2=キャンセル +int check_external_file( + char *ascii_buf, // 作業用 + wchar_t *file_path, // 作業用 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc) // スライス検査用の情報 +{ + wchar_t *file_name; + int list2_off = 0, len, find_num; + unsigned int meta_data[7]; + __int64 file_size; + HANDLE hFile; + WIN32_FILE_ATTRIBUTE_DATA AttrData; + +/*{ +FILE *fp; +fp = fopen("list_buf.txt", "wb"); +fwrite(list_buf, 2, list_len, fp); +fclose(fp); +}*/ + + wcscpy(file_path, base_dir); // 基準ディレクトリを入れておく + while (list2_off < list2_len){ + if (cancel_progress() != 0) // キャンセル処理 + return 2; + + file_name = list2_buf + list2_off; + len = (int)wcslen(file_name); + if (!GetFileAttributesEx(file_name, GetFileExInfoStandard, &AttrData)){ + list2_off += len + 1; + continue; + } + + // ファイル・サイズが最小ブロックよりも小さいファイルは無視する + file_size = ((__int64)(AttrData.nFileSizeHigh) << 32) | (__int64)(AttrData.nFileSizeLow); + if (file_size < (__int64)(sc->min_size)){ + list2_off += len + 1; + continue; + } + // 破損してないリカバリ・ファイルは無視する + if (search_file_path(recv_buf, recv_len, file_name)){ + list2_off += len + 1; + continue; + } + if (_wcsnicmp(base_dir, file_name, base_len) == 0){ // 同じディレクトリなら + // ソース・ファイルや検査済みの分割・類似名ファイルは無視する + // 破損したソース・ファイルの作業ファイルは無視する + if ((search_file_path(list_buf + 1, list_len - 1, file_name + base_len)) || + (avoid_temp_file(file_name + base_len, files))){ + list2_off += len + 1; + continue; + } + } + utf16_to_cp(file_name, ascii_buf, cp_output); + //printf("\n verifying, %s\n", ascii_buf); + + // ファイルを検査してスライスを探す + hFile = CreateFile(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile != INVALID_HANDLE_VALUE){ + prog_last = -1; // 経過表示がまだの印 + find_num = check_ini_verify(file_name, hFile, -1, meta_data, files, s_blk, sc); + if (find_num == -3){ // 検査結果の記録が無ければ + if (switch_v & 1){ // 簡易検査 + find_num = search_block_simple(file_path, -1, 0, file_name, + hFile, file_size, 0, 0, files, s_blk, sc); + } else { // 詳細検査 + find_num = search_block_slide(file_path, -1, 0, file_name, + hFile, file_size, 0, files, s_blk, sc); + } + // 検査結果を記録する + write_ini_verify2(-1, meta_data, find_num); + } + CloseHandle(hFile); // 検査したファイルを閉じる + if (find_num < 0){ + return find_num; // 検査時にエラーが発生した + } else if (find_num > 0){ // スライスが検出された時 + if (sc->flake_count > 0){ // スライス断片の一致を調べる + if (check_flake(file_path, files, s_blk, sc) != 0) + return -1; + } + print_progress_file(-1, first_num, NULL); // 重複を除外した利用可能なソース・ブロック数を表示する + + // 検査したファイル名を表示する + if (_wcsnicmp(base_dir, file_name, base_len) == 0){ // 基準ディレクトリ以下なら + utf16_to_cp(file_name + base_len, ascii_buf, cp_output); // 相対パスにする + printf("%13I64d Found : \"%s\"\n", file_size, ascii_buf); + json_add_found(file_name + base_len, 0); + } else { // 基準ディレクトリの外側なら + path_to_cp(file_name, ascii_buf, cp_output); // パスも表示する + printf("%13I64d External : \"%s\"\n", file_size, ascii_buf); + json_add_found(file_name, 1); + } + // ファイルごとに見つけたスライス数を表示する + show_file_slice(ascii_buf, files, s_blk); + fflush(stdout); + if (first_num >= source_num) // 全てのスライスが見つかれば抜ける + return 0; + } else { + print_progress_done(); // 経過表示があれば 100% にしておく + } + } + + list2_off += len + 1; // 次のファイルへ + } + + return 0; +} + +// 複数の別名ファイルに共通する部分があれば、他のファイルも同じと予想する +static int check_common_misname( + wchar_t *find_name, // 検索条件のファイル名が戻る + file_ctx_r *files) // 各ソース・ファイルの情報 +{ + wchar_t common_part[MAX_LEN], filename[MAX_LEN], *tmp_p; + int i, len, misname_count, lost_count; + + common_part[0] = 0; + misname_count = 0; + lost_count = 0; + for (i = 0; i < entity_num; i++){ + if ((files[i].state & 0x28) == 0x20){ // 別名なら (破損して別名は含めない) + wcscpy(filename, list_buf + files[i].name2); + if (wcschr(filename, '\\') != NULL) // サブ・ディレクトリを含む場合は判定不能 + return 0; + _wcslwr(filename); // 小文字に変換して比較する + //printf_cp("misname %s\n", filename); + if (common_part[0] == 0){ // 最初なら拡張子の「.」以降を省いてコピーする + wcscpy(common_part, filename); + tmp_p = wcsrchr(common_part + 1, '.'); // 先頭の「.」は無視する + if (tmp_p != NULL){ + if (wcslen(tmp_p) < EXT_LEN) // ピリオドを残して拡張子を取り除く + tmp_p[1] = 0; + } + } else { // 二個目以降は共通部分を比較する + len = 0; + while (common_part[len] == filename[len]){ + if (filename[len] == 0) + break; + len++; + } + if (len < 2) + return 0; // 最低でも 2文字は共通部分が必要 + common_part[len] = 0; + } + misname_count++; + } else if (files[i].state == 0x01){ // 消失 (チェックサム無しは無視する) + lost_count++; + } + } + + // 検索には、別名ファイルが二個以上、消失ファイルが一個以上必要 + if ((misname_count < 2) || (lost_count < 1)) + return 0; + + //printf("common = \"%S\", len = %d\n", common_part, len); + // 数値の途中なら、その手前までにする + // hoge.part1.txt と hoge.part10.txt の共通部分は「hoge.part1」だが、 + // 「hoge.part1*」で検索しても hoge.part2.txt は見つからない + while (len > 0){ + i = common_part[len - 1]; + if ((i < '0') || (i > '9')){ // 数値でなければいい + break; + } else { // 共通部分の末尾が数値なら取り除く + len--; + common_part[len] = 0; + } + } + if (len < 2) + return 0; // 最低でも 2文字は共通部分が必要 + + common_part[len ] = '*'; // 最後に検索文字をつける + common_part[len + 1] = 0; + wcscpy(find_name, common_part); + return 1; +} + +// 基準ディレクトリ内を検索して、名前が異なってるソース・ファイルを探す +// 名前の異なるファイルも詳細検査するので、アーカイブ内のスライスを検出できる +// 0=検査完了, -1=エラー, -2=キャンセル +int search_additional_file( + char *ascii_buf, // 作業用 + wchar_t *file_path, // 作業用 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc) // スライス検査用の情報 +{ + int find_num; + unsigned int meta_data[7]; + __int64 file_size; + HANDLE hFind, hFile; + WIN32_FIND_DATA FindData; + + wcscpy(file_path, base_dir); + if (switch_v & 2){ // 追加検査なら基準ディレクトリ直下のファイルを全て探す + wcscpy(file_path + base_len, L"*"); + } else { // 複数の別名ファイルに共通する部分があれば、それを検索条件にして追加検査する + if (check_common_misname(file_path + base_len, files) == 0) + return 0; // 追加検査しない設定で共通部分も無ければ、検索しない + } + + // 基準ディレクトリ内を検索する + //printf_cp("\n search path = %s \n", file_path); + hFind = FindFirstFile(file_path, &FindData); + if (hFind == INVALID_HANDLE_VALUE) + return 0; + do { + if (cancel_progress() != 0){ // キャンセル処理 + FindClose(hFind); + return 2; + } + + // フォルダは無視する + if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) + continue; + // ファイル・サイズが最小ブロックよりも小さいファイルは無視する + file_size = ((__int64)(FindData.nFileSizeHigh) << 32) | (__int64)(FindData.nFileSizeLow); + if (file_size < (__int64)(sc->min_size)) + continue; + // 破損してないリカバリ・ファイルは無視する + wcscpy(file_path + base_len, FindData.cFileName); + if (search_file_path(recv_buf, recv_len, file_path)) + continue; + // ソース・ファイルや検査済みの分割・類似名ファイルは無視する + if (search_file_path(list_buf + 1, list_len - 1, FindData.cFileName)) + continue; + // 破損したソース・ファイルの作業ファイルは無視する + if (avoid_temp_file(FindData.cFileName, files)) + continue; + // 検査済みの指定ファイルは無視する + if (list2_buf){ + if (search_file_path(list2_buf, list2_len, file_path)) + continue; + } + utf16_to_cp(FindData.cFileName, ascii_buf, cp_output); + //printf("\n verifying, %s\n", ascii_buf); + + // ファイルを検査してソース・ブロックを探す + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile != INVALID_HANDLE_VALUE){ + prog_last = -1; // 経過表示がまだの印 + find_num = check_ini_verify(FindData.cFileName, hFile, -1, meta_data, files, s_blk, sc); + if (find_num == -3){ // 検査結果の記録が無ければ + if (switch_v & 1){ // 簡易検査 + find_num = search_block_simple(file_path, -1, 0, FindData.cFileName, + hFile, file_size, 0, 0, files, s_blk, sc); + } else { // 詳細検査 + find_num = search_block_slide(file_path, -1, 0, FindData.cFileName, + hFile, file_size, 0, files, s_blk, sc); + } + // 検査結果を記録する + write_ini_verify2(-1, meta_data, find_num); + } + CloseHandle(hFile); // 検査したファイルを閉じる + if (find_num < 0){ + FindClose(hFind); + return find_num; // 検査時にエラーが発生した + } else if (find_num > 0){ // スライスが検出された時 + if (sc->flake_count > 0){ // スライス断片の一致を調べる + if (check_flake(file_path, files, s_blk, sc) != 0){ + FindClose(hFind); + return -1; + } + } + print_progress_file(-1, first_num, NULL); // 重複を除外した利用可能なソース・ブロック数を表示する + + // 検査したファイル名を表示する + utf16_to_cp(FindData.cFileName, ascii_buf, cp_output); + printf("%13I64d Found : \"%s\"\n", file_size, ascii_buf); + json_add_found(FindData.cFileName, 0); + // ファイルごとに見つけたスライス数を表示する + show_file_slice(ascii_buf, files, s_blk); + fflush(stdout); + if (first_num >= source_num){ // 全てのスライスが見つかれば抜ける + FindClose(hFind); + return 0; + } + } else { + print_progress_done(); // 経過表示があれば 100% にしておく + } + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// スライスを逆算するか、共通してるスライスを探す +int search_calculable_slice( + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + unsigned char hash[20]; + int i, j, k, num, b_last, lost_count, block_count; + int zero_count, reve_count, dupl_count; + int *order = NULL; + + block_count = 0; + lost_count = 0; + for (num = 0; num < entity_num; num++){ + if ((files[num].state & 0x80) == 0){ // チェックサムがあるファイルだけ比較する + b_last = files[num].b_off + files[num].b_num; + for (i = files[num].b_off; i < b_last; i++){ + if (s_blk[i].exist != 0){ + block_count++; // 比較可能なスライスの数 + } else { + lost_count++; // 失われてるスライスの数 + } + } + } + } + if (lost_count == 0) + return 0; // 比較しないで終わる + + if (block_size <= 4){ // ブロック・サイズが 4バイト以下なら全て逆算できる + reve_count = lost_count - first_num; + first_num += reve_count; + for (num = 0; num < entity_num; num++){ + if ((files[num].state & 0x80) == 0){ // チェックサムがあるファイルだけ比較する + if (files[num].state & 0x0A){ // 破損 0x02、破損して別名 0x28 なら + files[num].state &= 0xFF; // 上位 24-bit の記録を消しておく + // 本来は先頭の完全なスライスの状態は 2 だが、コピーせずに全て復元する + if (files[num].state & 0x02){ // 破損でも全てのスライスを利用できる + b_last = files[num].b_off + files[num].b_num; + for (i = files[num].b_off; i < b_last; i++) + s_blk[i].exist = 5; // CRC で内容を復元できる + } + } + } + } + printf("\nComparing lost slice\t: %d\n", reve_count); + printf("Reversible slice count\t: %d\n", reve_count); + fflush(stdout); + return reve_count; + } + + //printf("lost_count = %d, block_count = %d\n", lost_count, block_count); + if (block_count > 0){ + // CRC-32 の順序を格納するバッファーを確保する + order = (int *)malloc(block_count * sizeof(int) * 2); + if (order == NULL) + return 0; + block_count = 0; + for (num = 0; num < entity_num; num++){ + if ((files[num].state & 0x80) == 0){ // チェックサムがあるファイルだけ比較する + b_last = files[num].b_off + files[num].b_num; + for (i = files[num].b_off; i < b_last; i++){ + if (s_blk[i].exist != 0){ // 比較可能なスライス + order[block_count * 2 ] = i; // 最初はブロック番号にしておく + order[block_count * 2 + 1] = s_blk[i].crc; + block_count++; + } + } + } + } + // 昇順に並び替える + qsort(order, block_count, sizeof(int) * 2, sort_cmp_crc); + // [番号, CRC] の順を並び替えて [番号] だけにする + for (i = 1; i < block_count; i++) + order[i] = order[i * 2]; + } + + printf("\nComparing lost slice\t: %d within %d\n", lost_count, block_count); + fflush(stdout); + zero_count = 0; + reve_count = 0; + dupl_count = 0; + data_md5_crc32_zero(hash); // 全て 0のブロックのチェックサムを計算しておく + for (num = 0; num < entity_num; num++){ + if ((files[num].state & 0x80) == 0){ // チェックサムがあるファイルだけ比較する + b_last = files[num].b_off + files[num].b_num; + for (i = files[num].b_off; i < b_last; i++){ + if (s_blk[i].exist == 0){ // 失われてるスライス + if (memcmp(s_blk[i].hash, hash, 20) == 0){ + s_blk[i].exist = 3; // 内容が全て 0 のブロック + zero_count++; + first_num++; + //printf(" block[%d] = zero only\n", i); + + } else if (s_blk[i].size <= 4){ + // そのブロック内容が 4バイト以下なら逆算する + s_blk[i].exist = 5; // CRC で内容を復元できる + reve_count++; + first_num++; + //printf(" block[%d] = reversible\n", i); + + } else if (block_count > 0){ + // 失われたスライスと同じ内容のスライスを探す +/* for (j = 0; j < block_count; j++){ // linear search + k = order[j]; + if (s_blk[i].size == s_blk[k].size){ // サイズが一致すれば + if (memcmp(s_blk[i].hash, s_blk[k].hash, 20) == 0){ + // ブロックのチェックサムが一致するなら + s_blk[i].exist = 4; // 同じ内容のソース・ブロックが存在する + s_blk[i].file = k; // そのブロック番号を記録しておく + dupl_count++; + first_num++; + //printf(" lost block[%d] = existent block[%d]\n", i, k); + break; + } + } + } +*/ + j = binary_search(order, block_count, s_blk[i].crc, s_blk); // CRC-32 が一致する番号 + for (; j < block_count; j++){ // 複数ブロックが一致するかもしれないので + k = order[j]; + if (s_blk[i].crc != s_blk[k].crc) + break; // CRC-32 が一致しなくなったら抜ける + if (s_blk[i].size == s_blk[k].size){ // サイズが一致すれば + if (memcmp(s_blk[i].hash, s_blk[k].hash, 16) == 0){ + // ブロックのチェックサムが一致するなら + s_blk[i].exist = 4; // 同じ内容のソース・ブロックが存在する + s_blk[i].file = k; // そのブロック番号を記録しておく + dupl_count++; + first_num++; + //printf(" lost block[%d] = existent block[%d]\n", i, k); + break; + } + } + } + } + if (zero_count + reve_count + dupl_count >= lost_count) + break; // 十分な数のブロックを見つけたら抜ける + } + } + } + } + printf("Null byte slice count\t: %d\n", zero_count); + printf("Reversible slice count\t: %d\n", reve_count); + printf("Duplicate slice count\t: %d\n", dupl_count); + fflush(stdout); + + if (order) + free(order); + return zero_count + reve_count + dupl_count; +} + diff --git a/source/par2j/verify.h b/source/par2j/verify.h new file mode 100644 index 0000000..853bc51 --- /dev/null +++ b/source/par2j/verify.h @@ -0,0 +1,63 @@ +#ifndef _VERIFY_H_ +#define _VERIFY_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +// スライス検査の準備をする +int init_verification( + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ソース・ファイルのスライスを探す (分割ファイルも) +int check_file_slice( + char *ascii_buf, // 作業用 + wchar_t *file_path, // 作業用 + int num, // file_ctx におけるファイル番号 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc); // スライス検査用の情報 + +// 分割ファイルと類似名ファイルに含まれるスライスを探す +int search_file_split( + char *ascii_buf, // 作業用 + wchar_t *file_path, // 作業用 + int num, // file_ctx におけるファイル番号 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc); // スライス検査用の情報 + +// 指定された外部ファイルを検査する +int check_external_file( + char *ascii_buf, // 作業用 + wchar_t *file_path, // 作業用 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc); // スライス検査用の情報 + +// 基準ディレクトリ内を検索して、名前が異なってるソース・ファイルを探す +int search_additional_file( + char *ascii_buf, // 作業用 + wchar_t *find_path, // 作業用 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc); // スライス検査用の情報 + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// スライスを逆算するか、共通してるスライスを探す +int search_calculable_slice( + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par2j/version.h b/source/par2j/version.h new file mode 100644 index 0000000..c9e0ab1 --- /dev/null +++ b/source/par2j/version.h @@ -0,0 +1,2 @@ +#define FILE_VERSION "1.3.2.8" // ファイルのバージョン番号 +#define PRODUCT_VERSION "1.3.2" // 製品のバージョン番号