diff --git a/source/par2j/com.cpp b/source/par2j/com.cpp new file mode 100644 index 0000000..f85e1e8 --- /dev/null +++ b/source/par2j/com.cpp @@ -0,0 +1,176 @@ +// com.c +// Copyright : 2022-01-30 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 + +extern "C" { // C 言語の関数呼び出しに対応する + +/* +Sample code +https://docs.microsoft.com/ja-jp/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifileoperation-copyitem + +HRESULT CopyItem(__in PCWSTR pszSrcItem, __in PCWSTR pszDest, PCWSTR pszNewName) +{ + // + // Initialize COM as STA. + // + HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + if (SUCCEEDED(hr)) + { + IFileOperation *pfo; + + // + // Create the IFileOperation interface + // + hr = CoCreateInstance(CLSID_FileOperation, + NULL, + CLSCTX_ALL, + IID_PPV_ARGS(&pfo)); + if (SUCCEEDED(hr)) + { + // + // Set the operation flags. Turn off all UI from being shown to the + // user during the operation. This includes error, confirmation, + // and progress dialogs. + // + hr = pfo->SetOperationFlags(FOF_NO_UI); + if (SUCCEEDED(hr)) + { + // + // Create an IShellItem from the supplied source path. + // + IShellItem *psiFrom = NULL; + hr = SHCreateItemFromParsingName(pszSrcItem, + NULL, + IID_PPV_ARGS(&psiFrom)); + if (SUCCEEDED(hr)) + { + IShellItem *psiTo = NULL; + + if (NULL != pszDest) + { + // + // Create an IShellItem from the supplied + // destination path. + // + hr = SHCreateItemFromParsingName(pszDest, + NULL, + IID_PPV_ARGS(&psiTo)); + } + + if (SUCCEEDED(hr)) + { + // + // Add the operation + // + hr = pfo->CopyItem(psiFrom, psiTo, pszNewName, NULL); + + if (NULL != psiTo) + { + psiTo->Release(); + } + } + + psiFrom->Release(); + } + + if (SUCCEEDED(hr)) + { + // + // Perform the operation to copy the file. + // + hr = pfo->PerformOperations(); + } + } + + // + // Release the IFileOperation interface. + // + pfo->Release(); + } + + CoUninitialize(); + } + return hr; +} +*/ + +HRESULT DeleteItem(__in PCWSTR pszSrcItem) +{ + // + // Initialize COM as STA. + // + HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + if (SUCCEEDED(hr)) + { + IFileOperation *pfo; + + // + // Create the IFileOperation interface + // + hr = CoCreateInstance(CLSID_FileOperation, + NULL, + CLSCTX_ALL, + IID_PPV_ARGS(&pfo)); + if (SUCCEEDED(hr)) + { + // + // Set the operation flags. Turn off all UI from being shown to the + // user during the operation. This includes error, confirmation, + // and progress dialogs. + // + hr = pfo->SetOperationFlags(FOF_ALLOWUNDO | FOF_NO_UI); + if (SUCCEEDED(hr)) + { + // + // Create an IShellItem from the supplied source path. + // + IShellItem *psiFrom = NULL; + hr = SHCreateItemFromParsingName(pszSrcItem, + NULL, + IID_PPV_ARGS(&psiFrom)); + if (SUCCEEDED(hr)) + { + // + // Add the operation + // + hr = pfo->DeleteItem(psiFrom, NULL); + + psiFrom->Release(); + } + + if (SUCCEEDED(hr)) + { + // + // Perform the operation to delete the file. + // + hr = pfo->PerformOperations(); + } + } + + // + // Release the IFileOperation interface. + // + pfo->Release(); + } + + CoUninitialize(); + } + return hr; +} + + +} // end of extern "C" + diff --git a/source/par2j/common2.c b/source/par2j/common2.c new file mode 100644 index 0000000..392f0c2 --- /dev/null +++ b/source/par2j/common2.c @@ -0,0 +1,2608 @@ +// common2.c +// Copyright : 2022-10-13 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 + +#include "common2.h" + + +// グローバル変数 +wchar_t recovery_file[MAX_LEN]; // リカバリ・ファイルのパス +wchar_t base_dir[MAX_LEN]; // ソース・ファイルの基準ディレクトリ +wchar_t ini_path[MAX_LEN]; // 検査結果ファイルのパス + +int base_len; // ソース・ファイルの基準ディレクトリの長さ +int recovery_limit; // 作成時はリカバリ・ファイルのサイズ制限 +int first_num; // 作成時は最初のパリティ・ブロック番号、検査時は初めて見つけた数 + +int file_num; // ソース・ファイルの数 +int entity_num; // 実体のあるファイルの数 (recovery set に含まれるファイル数) +int recovery_num; // リカバリ・ファイルの数 +int source_num; // ソース・ブロックの数 +int parity_num; // パリティ・ブロックの数 + +unsigned int block_size; // ブロック・サイズ +unsigned int split_size; // 分割サイズ +__int64 total_file_size; // 合計ファイル・サイズ + +int switch_v; // 検査レベル 0=詳細検査, +1=簡易検査, +2=追加検査, 4=順列検査 + // +8=子フォルダを無視, +16=検出後に保存 +int switch_b; // バックアップを作るか + // 作成時はソース・ブロックの割合と基準サイズ + // 修復時 +1=別名にして残す, +2=ゴミ箱に入れる, +4=修復できなくても再構築 + // +16=完全ならPAR2削除 + +// 可変長サイズの領域にファイル名を記録する +wchar_t *list_buf; // ソース・ファイルのリスト +int list_len; // ファイル・リストの文字数 +int list_max; // ファイル・リストの最大文字数 + +wchar_t *recv_buf; // リカバリ・ファイルのリスト +int recv_len; // ファイル・リストの文字数 +wchar_t *recv2_buf; // 有効なパケットを含むリカバリ・ファイルのリスト +int recv2_len; // ファイル・リストの文字数 + +wchar_t *list2_buf; // 指定された検査対象のファイル名のリスト +int list2_len; +int list2_max; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +unsigned int cp_output; // Console Output Code Page + +// 指定された Code Page から UTF-16 に変換する +int cp_to_utf16(char *in, wchar_t *out, unsigned int cp) +{ + int err = 0; + unsigned int dwFlags = MB_ERR_INVALID_CHARS; // 不適切な文字列を警告する + + if (cp == CP_UTF8) + dwFlags = 0; + + if (MultiByteToWideChar(cp, dwFlags, in, -1, out, MAX_LEN) == 0){ + err = GetLastError(); + if ((dwFlags != 0) && (err == ERROR_INVALID_FLAGS)){ + //printf("MultiByteToWideChar, ERROR_INVALID_FLAGS\n"); + if (MultiByteToWideChar(cp, 0, in, -1, out, MAX_LEN) != 0) + return 0; + err = GetLastError(); + } + out[0] = 0; + return err; + } + return 0; +} + +// Windown OS の UTF-16 から指定された Code Page に変換する +int utf16_to_cp(wchar_t *in, char *out, unsigned int cp) +{ + unsigned int dwFlags = WC_NO_BEST_FIT_CHARS; // 似た文字への自動変換を行わない + + if (cp == CP_UTF8) + dwFlags = 0; + + if (WideCharToMultiByte(cp, dwFlags, in, -1, out, MAX_LEN * 3, NULL, NULL) == 0){ + if ((dwFlags != 0) && (GetLastError() == ERROR_INVALID_FLAGS)){ + //printf("WideCharToMultiByte, ERROR_INVALID_FLAGS\n"); + if (WideCharToMultiByte(cp, 0, in, -1, out, MAX_LEN * 3, NULL, NULL) != 0) + return 0; + } + strcpy(out, "cannot encode"); + return 1; + } + return 0; +} + +// Windows OS の UTF-8 から UTF-16 に変換する +void utf8_to_utf16(char *in, wchar_t *out) +{ + // UTF-16 <-> UTF-8 間では文字数以外のエラーは発生しない + if (MultiByteToWideChar(CP_UTF8, 0, in, -1, out, MAX_LEN) == 0) + wcscpy(out, L"too long"); +} + +// Windown OS の UTF-16 から UTF-8 に変換する +void utf16_to_utf8(wchar_t *in, char *out) +{ + // UTF-16 <-> UTF-8 間では文字数以外のエラーは発生しない + WideCharToMultiByte(CP_UTF8, 0, in, -1, out, MAX_LEN * 3, NULL, NULL); +} + +// 文字列が UTF-8 かどうかを判定する (0 = maybe UTF-8) +int check_utf8(unsigned char *text) +{ + unsigned char c1; + int tail_len = 0; // UTF8-tail が何バイト続くか + + while (*text != 0){ + c1 = *text; + // 禁止 0xC0~0xC1,0xF5~0xFF + if ((c1 == 0xC0) || (c1 == 0xC1) || (c1 >= 0xF5)) + return 1; // 禁止文字 + if (tail_len == 0){ // 第1バイトなら + // 1バイト文字 + if (c1 <= 0x7F){ + tail_len = 0; + // 2バイト文字の第1バイト + } else if ((c1 >= 0xC2) && (c1 <= 0xDF)){ + tail_len = 1; + // 3バイト文字の第1バイト + } else if ((c1 >= 0xE0) && (c1 <= 0xEF)){ + tail_len = 2; + // 4バイト文字の第1バイト + } else if (c1 >= 0xF0){ + tail_len = 3; + } else { + return 2; // 第1バイトとして不適当な文字 + } + } else { // 第2バイト以後なら + if ((c1 >= 0x80) && (c1 <= 0xBF)){ + tail_len--; + } else { + return 2; // 第2バイト以後として不適当な文字 + } + } + text++; // 次の文字へ + } + return 0; +} + +// ファイル・パスから、先頭にある "\\?\" を省いて、指定された Code Page に変換する +int path_to_cp(wchar_t *path, char *out, unsigned int cp) +{ + unsigned int dwFlags = WC_NO_BEST_FIT_CHARS; // 似た文字への自動変換を行わない + + if (cp == CP_UTF8) + dwFlags = 0; + + if (wcsncmp(path, L"\\\\?\\", 4) == 0){ // "\\?\" を省く + path += 4; + if (wcsncmp(path, L"UNC\\", 4) == 0){ // "\\?\UNC" を省いて "\" を追加する + path += 3; + *out = '\\'; + out += 1; + } + } + + if (WideCharToMultiByte(cp, dwFlags, path, -1, out, MAX_LEN * 3, NULL, NULL) == 0){ + if ((dwFlags != 0) && (GetLastError() == ERROR_INVALID_FLAGS)){ + //printf("WideCharToMultiByte, ERROR_INVALID_FLAGS\n"); + if (WideCharToMultiByte(cp, 0, path, -1, out, MAX_LEN * 3, NULL, NULL) != 0) + return 0; + } + strcpy(out, "cannot encode"); + return 1; + } + return 0; +} + +// UTF-16 のファイル・パスを画面出力用の Code Page を使って表示する +void printf_cp(unsigned char *format, wchar_t *path) +{ + unsigned char buf[MAX_LEN * 3]; + path_to_cp(path, buf, cp_output); + printf(format, buf); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ファイルの offset バイト目から size バイトのデータを buf に読み込む +int file_read_data( + HANDLE hFileRead, + __int64 offset, + unsigned char *buf, + unsigned int size) +{ + unsigned int rv; + + // ファイルの位置を offsetバイト目にする + if (!SetFilePointerEx(hFileRead, *((PLARGE_INTEGER)&offset), NULL, FILE_BEGIN)){ + print_win32_err(); + return 1; + } + + // size バイトを読み込む + if (!ReadFile(hFileRead, buf, size, &rv, NULL)){ + print_win32_err(); + return 1; + } + if (size != rv) + return 1; // 指定サイズを読み込めなかったらエラーになる + + return 0; +} + +// ファイルの offset バイト目に size バイトのデータを buf から書き込む +int file_write_data( + HANDLE hFileWrite, + __int64 offset, + unsigned char *buf, + unsigned int size) +{ + unsigned int rv; + + // ファイルの位置を offsetバイト目にする + if (!SetFilePointerEx(hFileWrite, *((PLARGE_INTEGER)&offset), NULL, FILE_BEGIN)){ + print_win32_err(); + return 1; + } + + // size バイトを書き込む + if (!WriteFile(hFileWrite, buf, size, &rv, NULL)){ + print_win32_err(); + return 1; + } + + return 0; +} + +// ファイルの offset バイト目に size バイトの指定値を書き込む +int file_fill_data( + HANDLE hFileWrite, + __int64 offset, + unsigned char value, + unsigned int size) +{ + unsigned char buf[IO_SIZE]; + unsigned int rv, len; + + // ファイルの位置を offsetバイト目にする + if (!SetFilePointerEx(hFileWrite, *((PLARGE_INTEGER)&offset), NULL, FILE_BEGIN)){ + print_win32_err(); + return 1; + } + + // 指定された値で埋める + memset(buf, value, IO_SIZE); + while (size){ + len = IO_SIZE; + if (size < IO_SIZE) + len = size; + size -= len; + if (!WriteFile(hFileWrite, buf, len, &rv, NULL)){ + print_win32_err(); + return 1; + } + } + + return 0; +} + +// ファイルの指定バイト目から size バイトのデータを別のファイルに書き込む +int file_copy_data( + HANDLE hFileRead, + __int64 offset_read, + HANDLE hFileWrite, + __int64 offset_write, + unsigned int size) +{ + unsigned char buf[IO_SIZE]; + unsigned int rv, len; + + // ファイルの位置を offset_read バイト目にする + if (!SetFilePointerEx(hFileRead, *((PLARGE_INTEGER)&offset_read), NULL, FILE_BEGIN)){ + print_win32_err(); + return 1; + } + + // ファイルの位置を offset_write バイト目にする + if (!SetFilePointerEx(hFileWrite, *((PLARGE_INTEGER)&offset_write), NULL, FILE_BEGIN)){ + print_win32_err(); + return 1; + } + + // コピーする + while (size){ + len = IO_SIZE; + if (size < IO_SIZE) + len = size; + size -= len; + if (!ReadFile(hFileRead, buf, len, &rv, NULL)){ + print_win32_err(); + return 1; // 指定サイズを読み込めなくてもエラーにしない + } + if (rv < len){ + memset(buf + rv, 0, len - rv); // 足りなかった分は 0 で埋める + size = 0; + } + if (!WriteFile(hFileWrite, buf, len, &rv, NULL)){ + print_win32_err(); + return 1; + } + } + + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 修復中のテンポラリ・ファイルの名前を作る +// 末尾に _par.tmp を追加する (ADD_LEN 以下の文字数) +void get_temp_name( + wchar_t *file_path, // ファイル・パス + wchar_t *temp_path) // テンポラリ・ファイルのパス +{ + wcscpy(temp_path, file_path); + wcscat(temp_path, L"_par.tmp"); // 末尾に追加する +} + +// 作業用のゼロで埋められたテンポラリ・ファイルを作成する +int create_temp_file( + wchar_t *file_path, // ファイルのパス + __int64 file_size) // ファイルのサイズ +{ + HANDLE hFile; + + hFile = CreateFile(file_path, 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(file_path); // 途中のフォルダが存在しないのなら作成する + hFile = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + return 1; + } + } + + // 本来のサイズにする + if (!SetFilePointerEx(hFile, *((PLARGE_INTEGER)&file_size), NULL, FILE_BEGIN)){ + print_win32_err(); + CloseHandle(hFile); + return 1; + } + if (!SetEndOfFile(hFile)){ + print_win32_err(); + CloseHandle(hFile); + return 1; + } + CloseHandle(hFile); + + return 0; +} + +#define FILE_WAIT_SEC 200 // ファイルアクセスの待ち時間 ms +#define FILE_WAIT_NUM 5 + +// 作業用のソース・ファイルを開く +HANDLE handle_temp_file( + wchar_t *file_name, // ソース・ファイル名 + wchar_t *file_path) // 作業用、基準ディレクトリが入ってる +{ + int rv, retry_time = FILE_WAIT_NUM; + HANDLE hFile; + + get_temp_name(file_name, file_path + base_len); + // 作業ファイル内でデータをコピーできるよう、SHARE_READ にする + hFile = CreateFile(file_path, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + while (hFile == INVALID_HANDLE_VALUE){ + rv = GetLastError(); + if ((rv == 32) || // ERROR_SHARING_VIOLATION + (rv == 33)){ // ERROR_LOCK_VIOLATION + Sleep(FILE_WAIT_SEC); // 少し待ってから再挑戦する + hFile = CreateFile(file_path, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + } else { + break; + } + retry_time--; + if (retry_time == 0) + break; + } + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + printf_cp("cannot open file, %s\n", file_path); + } + + return hFile; +} + +// 上書き用のソース・ファイルを開く +HANDLE handle_write_file( + wchar_t *file_name, // ソース・ファイル名 + wchar_t *file_path, // 作業用、基準ディレクトリが入ってる + __int64 file_size) // 本来のファイルサイズ +{ + __int64 now_size; + HANDLE hFile; + + wcscpy(file_path + base_len, file_name); + + // ソースファイル内でデータをコピーできるよう、SHARE_READ にする + // 既存ファイルの内容と属性を維持するため OPEN_ALWAYS にする + hFile = CreateFile(file_path, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + } else { + // 現在のファイルサイズを取得する + if (GetFileSizeEx(hFile, (PLARGE_INTEGER)&now_size) == 0){ + print_win32_err(); + CloseHandle(hFile); + hFile = INVALID_HANDLE_VALUE; + // 大きすぎる場合は本来のサイズにする(小さい場合は自動的に埋められる) + } else if (now_size > file_size){ + if (SetFilePointerEx(hFile, *((PLARGE_INTEGER)&file_size), NULL, FILE_BEGIN) == 0){ + print_win32_err(); + CloseHandle(hFile); + hFile = INVALID_HANDLE_VALUE; + } else if (SetEndOfFile(hFile) == 0){ + print_win32_err(); + CloseHandle(hFile); + hFile = INVALID_HANDLE_VALUE; + } + } + } + if (hFile == INVALID_HANDLE_VALUE) + printf_cp("cannot open file, %s\n", file_path); + + return hFile; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ファイル・パスがファイル・リスト上に既に存在するか調べる +int search_file_path( + wchar_t *list, // ファイル・リスト + int total_len, // ファイル・リストの文字数 + wchar_t *search_file) // 検索するファイルのパス +{ + int off = 0; + + while (off < total_len){ + if (_wcsicmp(list + off, search_file) == 0) + return 1; + // 次のファイル名の位置へ + //off += (wcslen(list + off) + 1); + while (list[off] != 0) + off++; + off++; + } + + return 0; +} + +// ファイル・リストの内容を並び替える +void sort_list( + wchar_t *list, // ファイル・リスト + int total_len) // ファイル・リストの文字数 +{ + wchar_t *work_buf; + int off1 = 0, off2, off3, work_off = 0; + + // 作業バッファーを確保する + work_buf = (wchar_t *)calloc(total_len, 2); + if (work_buf == NULL) + return; // 並べ替え失敗 + + while (off1 < total_len){ + if (list[off1] == 0){ + break; + } else if (list[off1] == '*'){ // 既にコピー済なら + //off1 += (wcslen(list + off1) + 1); + while (list[off1] != 0) + off1++; + off1++; + continue; + } + off3 = off1; + + //off2 = off3 + wcslen(list + off3) + 1; // 次の項目 + off2 = off3; + while (list[off2] != 0) + off2++; + off2++; + while (off2 < total_len){ +//printf("%S (%d), %S (%d)\n", list + off3, off3, list + off2, off2); + if (list[off2] != '*'){ // まだなら項目を比較する + //if (wcscmp(list + off3, list + off2) > 0) // QuickPar はこちらの順序 + //if (CompareString(LOCALE_USER_DEFAULT, 0, list + off3, -1, list + off2, -1) > 2) + // Windows 7 以降では数値を認識できる SORT_DIGITSASNUMBERS = 0x00000008 + if (CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0x00000008, list + off3, -1, list + off2, -1, NULL, NULL, 0) > 2) + off3 = off2; + } + //off2 += (wcslen(list + off2) + 1); // 次の項目 + while (list[off2] != 0) + off2++; + off2++; + } + + // 順番にコピーしていく +//printf("get %S (%d)\n", list + off3, off3); + wcscpy(work_buf + work_off, list + off3); + work_off += ((int)wcslen(work_buf + work_off) + 1); + list[off3] = '*'; // コピーした印 + if (off3 == off1){ + //off1 += (wcslen(list + off1) + 1); + while (list[off1] != 0) + off1++; + off1++; + } + } + + // 作業バッファーから戻す + memcpy(list, work_buf, total_len * 2); + free(work_buf); +} + +// ソース・ファイルのリストに新しいファイル名を追加する +int add_file_path(wchar_t *filename) // 追加するファイル名 +{ + wchar_t *tmp_p; + int len; + + len = (int)wcslen(filename); + + if (list_len + len >= list_max){ // 領域が足りなくなるなら拡張する + list_max += ALLOC_LEN; + tmp_p = (wchar_t *)realloc(list_buf, list_max * 2); + if (tmp_p == NULL){ + return 1; + } else { + list_buf = tmp_p; + } + } + + wcscpy(list_buf + list_len, filename); + list_len += len + 1; + + return 0; +} + +// ファイル・リストから指定されたファイル・パスを取り除く +// 減らした後のファイル・リストの文字数を返す +int remove_file_path( + wchar_t *list, // ファイル・リスト + int total_len, // ファイル・リストの文字数 + int file_off) // 取り除くファイル・パスの位置 +{ + int off, len; + + if (file_off > total_len) + return total_len; + + len = (int)wcslen(list + file_off) + 1; // 末尾のNULLを含める + if (len < total_len){ + for (off = file_off; off < total_len - len; off++) + list[off] = list[off + len]; + for (off = total_len - len; off < total_len; off++) + list[off] = 0; + return total_len - len; + } else { + for (off = 0; off < total_len; off++) + list[off] = 0; + return 0; + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ファイル・パスからファイル名の位置を戻す +wchar_t * offset_file_name(wchar_t *file_path) +{ + int i; + + for (i = (int)wcslen(file_path) - 2; i >= 0; i--){ + if ((file_path[i] == '\\') || (file_path[i] == '/')) + break; + } + i++; + return file_path + i; +} + +// ファイル・パスからファイル名だけ取り出す +void get_file_name( + wchar_t *file_path, // ファイル・パス + wchar_t *file_name) // ファイル名 +{ + int i, len, find = -1; + + len = (int)wcslen(file_path); + for (i = len - 2; i >= 0; i--){ + if ((file_path[i] == '\\') || (file_path[i] == '/')){ + find = i; + break; + } + } + find++; + for (i = find; i < len + 1; i++){ + file_name[i - find] = file_path[i]; + } +} + +// ファイル・パスからディレクトリだけ取り出す、末尾は「\」か「/」 +void get_base_dir( + wchar_t *file_path, // ファイル・パス + wchar_t *base_path) // ディレクトリ +{ + int i, len, find = 0; + + len = (int)wcslen(file_path); + if (len <= 1){ + base_path[0] = 0; + return; + } + + // 最初から末尾が「\」か「/」なら + if ((file_path[len - 1] == '\\') || (file_path[len - 1] == '/')) + len--; // それを取り除いてひとつ親のディレクトリを返す + for (i = len - 1; i >= 0; i--){ + if (find == 0){ + if ((file_path[i] == '\\') || (file_path[i] == '/')){ + find = 1; + base_path[i] = file_path[i]; + base_path[i + 1] = 0; + } + } else { + base_path[i] = file_path[i]; + } + } + if (find == 0) + base_path[0] = 0; +} + +// ディレクトリ記号の「\」を「/」に置換する +void unix_directory(wchar_t *path) +{ + int i = 0; + + while (path[i] != 0){ + if (path[i] == '\\') + path[i] = '/'; + i++; + } +} + +// 絶対パスかどうかを判定する +int is_full_path(wchar_t *path) +{ + if ((path[0] == 0) || (path[1] == 0)) + return 0; // 2文字以下だと短すぎ + + // 通常のドライブ指定「c:\」の形式 + if ((path[1] == ':') && ((path[2] == '\\') || (path[2] == '/'))) + return 1; + + //「\\?\」が付くのは絶対パスだけ + // ネットワークの UNC パスなら「\\\」 + if (((path[0] == '\\') || (path[0] == '/')) && ((path[1] == '\\') || (path[1] == '/'))) + return 1; + + return 0; +} + +// ファイルのディレクトリ位置が同じかどうかを調べる +// フォルダには対応してないことに注意! +int compare_directory(wchar_t *path1, wchar_t *path2) +{ + int len1, len2; + + // パスの長さ + len1 = 0; + while (path1[len1] != 0) + len1++; + // フォルダの位置を調べたい時は末尾の「\」を無視する +// if (accept_folder != 0) +// len1--; + // サブ・ディレクトリの長さにする + while ((len1 > 0) && (path1[len1] != '\\')) + len1--; + + // パスの長さ + len2 = 0; + while (path2[len2] != 0) + len2++; + // フォルダの位置を調べたい時は末尾の「\」を無視する +// if (accept_folder != 0) +// len2--; + // サブ・ディレクトリの長さにする + while ((len2 > 0) && (path2[len2] != '\\')) + len2--; + + // サブ・ディレクトリの長さを比較する + if (len1 > len2) + return 1; // path1 が path2 より大きい + if (len1 < len2) + return -1; // path1 が path2 より小さい + + // ディレクトリの長さが同じなら + return _wcsnicmp(path1, path2, len1); +} + +// ワイルドカードを含む文字列をコピーする +// 出力文字数を返す (null文字を含む)、不正なパスなら 0 を返す +int copy_wild(wchar_t *dst, wchar_t *src) +{ + wchar_t s; + int len = 0; + + while ((src[0] == '\\') || (src[0] == '/')) // 先頭の「\」は無視する + src++; + + do { + s = src[0]; + if (s == '/'){ // ディレクトリ記号を統一する + s = '\\'; + if ((src[1] == '\\') || (src[1] == '/')) + return 0; + } else if (s == '\\'){ // 「\\」はパスに含まれない + if ((src[1] == '\\') || (src[1] == '/')) + return 0; + } else if ((s >= 'A') && (s <= 'Z')){ // 大文字を小文字に変換する + s += 32; + } else if ((s == '*') && (src[1] == '*')){ // 三個以上の「*」を一個にする + while (src[2] == '*') + src++; + } else if ((s == '.') && (src[1] == '.')){ // 「..」はパスに含まれない + return 0; + } + + dst[len++] = s; + src++; + } while (s != 0); + + return len; // 末尾の null 文字を個数に含むことに注意 +} + +// ワイルドカード('*', '?')を使ってユニコードのパスを比較する +// 「*」と「?」はディレクトリ記号に一致しない +// 「**」はディレクトリ記号に一致する +int PathMatchWild( + wchar_t *text, // 比較する文字列 + wchar_t *wild) // ワイルドカード (小文字にすること) +{ + wchar_t next_char, *wild_prev, *wild_dir, *text_dir; + int stop_dir; + + next_char = 0; + wild_prev = NULL; + wild_dir = NULL; + text_dir = text; + stop_dir = 0; + + while (*text != 0){ + //printf("%c, %c \n", *text, *wild); + if (*wild == '*'){ + wild++; + if (*wild == '*'){ // 「**」 + while (*wild == '*') // 二個より連続した「*」を無視する + wild++; + stop_dir = 0; + wild_dir = wild; + } else { // 「*」 + stop_dir = 1; + } + + if (*wild == 0){ // 「*」の次が無ければ一致とみなす + if (stop_dir){ // ディレクトリ記号が存在すれば不一致 + while (*text != 0){ + if (*text == '\\'){ + stop_dir = 2; + break; + } + text++; + } + if (stop_dir == 2){ + if (wild_dir != NULL){ + stop_dir = 0; + wild_prev = wild_dir; + text = text_dir + 1; + wild = wild_dir; + next_char = *wild; + //printf("\n agian last: %c, %c \n", *text, next_char); + continue; + } + //printf("\n last * != include \\ \n"); + return 0; + } + } + return 1; + } else { + next_char = *wild; // 「*」の次の文字 + wild_prev = wild; // 最後の「*」の位置 + //printf("next = %c, stop_dir = %d\n", next_char, stop_dir); + } + } + + if ((*text == '\\') && (stop_dir)) // ディレクトリ記号が存在するなら再比較をしない + wild_prev = NULL; + + if (next_char != 0){ // 「*」の後なら + if ((next_char == towlower(*text)) || ((*text != '\\') && (next_char == '?'))){ + wild++; + next_char = 0; + text_dir = text; + } + if ((*text == '\\') && (stop_dir)) // ディレクトリ記号が存在するならワイルドカードを無効にする + next_char = 0; + text++; + + } else if ((*wild == towlower(*text)) || ((*text != '\\') && (*wild == '?'))){ // 同じ文字なら + wild++; + text++; + + } else { // 違う文字なら + if (wild_prev != NULL){ // 直前のワイルドカードから比較しなおす + wild = wild_prev; + next_char = *wild; + //printf("\n agian: %c, %c \n", *text, next_char); + + } else if ((stop_dir != 0) && (wild_dir != NULL)){ + stop_dir = 0; + wild_prev = wild_dir; + text = text_dir + 1; + wild = wild_dir; + next_char = *wild; + //printf("\n agian dir: %c, %c \n", *text, next_char); + + } else{ + //printf("\n diff: %c, %c \n", *text, *wild); + return 0; // それ以上一致しない + } + } + } + + while (*wild == '*') // 残りの「*」を無視する + wild++; + if (*wild == 0) // ワイルドカードも終端なら一致とみなす + return 1; + + return 0; +} + +/* +int PathMatchWild1( + wchar_t *text, // 比較する文字列 + wchar_t *wild) // ワイルドカード (小文字にすること) +{ + wchar_t next_char, *wild_prev; + + next_char = 0; + wild_prev = NULL; + + while (*text != 0){ + //printf("%c, %c \n", *text, *wild); + + if (*wild == '*'){ + wild++; + while (*wild == '*') // 連続した「*」を無視する + wild++; + + if (*wild == 0){ // 「*」の次が無ければ一致とみなす + return 1; + } else { + next_char = *wild; // 「*」の次の文字 + wild_prev = wild; // 最後の「*」の位置 + } + } + + if (next_char != 0){ // 「*」の後なら + if ((next_char == towlower(*text)) || (next_char == '?')){ + wild++; + next_char = 0; + } + text++; + + } else if ((*wild == towlower(*text)) || (*wild == '?')){ // 同じ文字なら + wild++; + text++; + + } else { // 違う文字なら + if (wild_prev != NULL){ // 直前のワイルドカードから比較しなおす + wild = wild_prev; + next_char = *wild; + //printf("\n agian: %c, %c \n", *text, next_char); + + } else{ + return 0; // それ以上一致しない + } + } + } + + while (*wild == '*') // 残りの「*」を無視する + wild++; + if (*wild == 0) // ワイルドカードも終端なら一致とみなす + return 1; + + return 0; +} +*/ + +// ファイルのパスを除外リストと比較する +int exclude_path(wchar_t *path) +{ + int off = 0, deny = 0; + + deny = list2_max; + while (off < list2_len){ + if (list2_buf[off++] == '+'){ // allow + if ((deny != 0) && (PathMatchWild(path, list2_buf + off) != 0)){ + // printf_cp("allow: \"%s\"", path); + // printf_cp(" : \"%s\"\n", list2_buf + off); + deny = 0; + } + } else { // deny + if (PathMatchWild(path, list2_buf + off) != 0){ + // printf_cp("deny : \"%s\"", path); + // printf_cp(" : \"%s\"\n", list2_buf + off); + return 1; + } + } + off += (int)wcslen(list2_buf + off) + 1; + } + +// if ((list2_max != 0) && (deny != 0)) +// printf_cp("not : \"%s\"\n", path); + + return deny; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define PREFIX_LEN 4 // 「\\?\」の長さ + +// 相対パスを絶対パスに変換し、パスの先頭に "\\?\" を追加する +// 戻り値 : 0=エラー, 5~=新しいパスの長さ +int copy_path_prefix( + wchar_t *new_path, // 新しいパス + int max_len, // 新しいパスの最大長さ (末尾の null文字も含む) + wchar_t *src_path, // 元のパス (相対パスでもよい) + wchar_t *dir_path) // 相対パスの場合に基準となるディレクトリ (NULL ならカレント・ディレクトリ) +{ + wchar_t tmp_path[MAX_LEN], *src_name, *new_name; + int len; + + // 不正なファイル名を拒否する (検索記号は許可する) + if (src_path[0] == 0) + return 0; + if (wcspbrk(src_path, L"\"<>|")) // WinOS ではファイル名に「"<>|」の文字は使えない + return 0; + + // 不適切な名前のファイルは、変換時に浄化されることがある + src_name = offset_file_name(src_path); + //wprintf(L"src_name = \"%s\"\n", src_name); + + // 相対パスに対して基準となるディレクトリが指定されてるなら + if ((dir_path != NULL) && (is_full_path(src_path) == 0)){ + // そのまま基準ディレクトリを連結して絶対パスにする + len = (int)wcslen(dir_path); + if (len + (int)wcslen(src_path) >= max_len) + return 0; + wcscpy(tmp_path, dir_path); // dir_path の末尾は「\」にしておくこと + wcscpy(tmp_path + len, src_path); + if (GetFullPathName(tmp_path, max_len, new_path, &new_name) == 0) + return 0; + } else { // カレント・ディレクトリを使う + // 相対パスから絶対パスに変換する (ディレクトリ記号も「\」に統一される) + if (GetFullPathName(src_path, max_len, new_path, &new_name) == 0) + return 0; + } + + // 元のファイル名と比較して、浄化を取り消す + if (new_name == NULL) + new_name = offset_file_name(new_path); + //wprintf(L"new_name = \"%s\"\n", new_name); + if (_wcsicmp(src_name, new_name) != 0){ + len = (int)wcslen(new_name); + if ((_wcsnicmp(src_name, new_name, len) == 0) && ((src_name[len] == ' ') || (src_name[len] == '.'))){ + len = (int)wcslen(src_name); + if ((src_name[len - 1] == ' ') || (src_name[len - 1] == '.')){ + // 末尾の「 」や「.」が取り除かれてるなら元に戻す + wcscpy(new_name, src_name); + } + } + } + + // 先頭に "\\?\" が存在しないなら、追加する + if ((wcsncmp(new_path, L"\\\\?\\", PREFIX_LEN) != 0)){ + len = (int)wcslen(new_path); + if ((new_path[0] == '\\') && (new_path[1] == '\\')){ // UNC パスなら + if (len + PREFIX_LEN + 2 >= max_len) + return 0; // バッファー・サイズが足りなくて追加できない + memmove(new_path + (PREFIX_LEN + 3), new_path + 1, len * 2); + new_path[PREFIX_LEN ] = 'U'; + new_path[PREFIX_LEN + 1] = 'N'; + new_path[PREFIX_LEN + 2] = 'C'; + } else { // 通常のドライブ記号で始まるパスなら + if (len + PREFIX_LEN >= max_len) + return 0; // バッファー・サイズが足りなくて追加できない + memmove(new_path + PREFIX_LEN, new_path, (len + 1) * 2); + } + memcpy(new_path, L"\\\\?\\", PREFIX_LEN * 2); + } + + // 8.3形式の短いファイル名を長いファイル名に変換する + if (GetFileAttributes(new_path) == INVALID_FILE_ATTRIBUTES){ + // 指定されたファイルが存在しない場合 (作成するPARファイルや「*?」で検索するソース・ファイル) + for (len = PREFIX_LEN; new_path[len] != 0; len++){ + if ((new_path[len] == '*') || (new_path[len] == '?')) + break; // ワイルドカードを含むパス + } + // ワイルドカードより前のディレクトリ記号を探す + for (; len >= 0; len--){ + if (new_path[len] == '\\') + break; + } + len++; + wcscpy(tmp_path, new_path + len); // 存在しないファイル名を記録しておく + new_path[len] = 0; // ファイル名を消す + len = GetLongPathName(new_path, new_path, max_len); + if (len == 0){ // フォルダーやファイルが存在しなくてもエラー終了しない + //printf("GetLongPathName (not exist) : err = %d\n", GetLastError()); + //print_win32_err(); + wcscat(new_path, tmp_path); + len = (int)wcslen(new_path); + } else { // 退避させておいたファイル名を追加する + len += (int)wcslen(tmp_path); + if (len < max_len) + wcscat(new_path, tmp_path); + } + } else { + len = GetLongPathName(new_path, new_path, max_len); + if (len == 0){ // 変換エラー + //printf("GetLongPathName (exist) : err = %d\n", GetLastError()); + return 0; + } + } + if (len >= max_len) + return 0; + + return len; +} + +// ファイル・パスから、先頭にある "\\?\" を省いた長さを戻す +int len_without_prefix(wchar_t *file_path) +{ + // 最初から "\\?\" が無ければ + if (wcsncmp(file_path, L"\\\\?\\", PREFIX_LEN) != 0) + return (int)wcslen(file_path); // そのままの長さ + + // "\\?\UNC\\" ならネットワーク・パス + if (wcsncmp(file_path + PREFIX_LEN, L"UNC\\", 4) == 0) + return (int)wcslen(file_path + PREFIX_LEN + 2); // "\\?\UNC\" -> "\\" なので6文字減らす + + // "\\?\\" なら + return (int)wcslen(file_path + PREFIX_LEN); // "\\?\" を省くので4文字減らす +} + +// ファイル・パスから、先頭にある "\\?\" を省いてコピーする +int copy_without_prefix( + wchar_t *dst_path, // コピー先 (コピー元と同じアドレスでもよい) + wchar_t *src_path) // コピー元のパス +{ + int i, len; + + len = (int)wcslen(src_path); // 元の文字数 + + // 最初から "\\?\" が無ければ + if (wcsncmp(src_path, L"\\\\?\\", PREFIX_LEN) != 0){ + for (i = 0; i <= len; i++) + dst_path[i] = src_path[i]; // そのままコピーする + return len; + } + + // "\\?\UNC\\" ならネットワーク・パス + if (wcsncmp(src_path + PREFIX_LEN, L"UNC\\", 4) == 0){ + len -= PREFIX_LEN + 2; // "\\?\UNC\" -> "\\" なので6文字減らす + for (i = 1; i <= len; i++) + dst_path[i] = src_path[PREFIX_LEN + 2 + i]; + dst_path[0] = '\\'; // 先頭を "\\" に変えておく + return len; + } + + // "\\?\\" なら + len -= PREFIX_LEN; // "\\?\UNC\" -> "\\" なので6文字減らす + for (i = 0; i <= len; i++) + dst_path[i] = src_path[PREFIX_LEN + i]; + return len; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 順番にサブ・ディレクトリを作成する +int make_dir( + wchar_t *file_path) // サブ・ディレクトリを含むファイル・パス +{ + wchar_t dir_path[MAX_LEN]; + unsigned int rv; + + get_base_dir(file_path, dir_path); // 親ディレクトリを取得する + if (dir_path[0] == 0) + return 0; // ディレクトリがないなら出る + rv = (int)wcslen(dir_path); + if ((dir_path[rv - 1] == '\\') || (dir_path[rv - 1] == '/')) // 末尾の「/」を取り除く + dir_path[rv - 1] = 0; + + rv = GetFileAttributes(dir_path); + if (rv == INVALID_FILE_ATTRIBUTES){ // フォルダがまだ存在しない場合 + // 先に親フォルダを作成する + if (rv = make_dir(dir_path)) + return rv; + if (!CreateDirectory(dir_path, NULL)) + return 1; + } else if ((rv & FILE_ATTRIBUTE_DIRECTORY) == 0){ + // すでに同じ名前のファイルが存在する場合はエラーが発生する + return 2; + } + return 0; +} + +// ファイルを置き換える (ファイル名の修正、テンポラリー・ファイルからの書き戻しなど) +int replace_file( + wchar_t *dest_path, // 置き換える先のパス (移動先、修正されたファイル名) + wchar_t *sorc_path) // 置き換える元のパス (移動元、現在のファイル名) +{ + unsigned int rv, num, retry_time = FILE_WAIT_NUM; + + // 移動元のファイルが存在するかどうか + num = GetFileAttributes(sorc_path); + if (num == INVALID_FILE_ATTRIBUTES){ + return 1; // 移動させるファイルが存在しない + } else { + if (num & FILE_ATTRIBUTE_DIRECTORY){ // 対象がフォルダなら末尾の「\」を取り除く + rv = (unsigned int)wcslen(sorc_path); + if (sorc_path[rv - 1] == '\\') + sorc_path[rv - 1] = 0; + rv = (unsigned int)wcslen(dest_path); + if (dest_path[rv - 1] == '\\') + dest_path[rv - 1] = 0; + } + if (num & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性なら、その属性を解除する + if (SetFileAttributes(sorc_path, num ^ FILE_ATTRIBUTE_READONLY) == 0) + return 2; // 移動元の読み取り専用属性を解除できなかった + } + } + //printf_cp("dest_path = %s\n", dest_path); + //printf_cp("sorc_path = %s\n", sorc_path); + + // 移動先のファイルが存在するかどうか + rv = GetFileAttributes(dest_path); + if (rv == INVALID_FILE_ATTRIBUTES){ // ファイルが存在しないなら、そのまま移動する + if (MoveFile(sorc_path, dest_path) == 0){ + if (GetLastError() == ERROR_PATH_NOT_FOUND){ // Path not found (3) + make_dir(dest_path); // 途中のフォルダが存在しないのなら作成する + if (MoveFile(sorc_path, dest_path) == 0) + return 3; // それでも移動失敗 + } else { + return 4; // 移動失敗 + } + } + if (num & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性を付け直す + rv = GetFileAttributes(dest_path); + SetFileAttributes(dest_path, rv | FILE_ATTRIBUTE_READONLY); // 失敗してもエラーにしない + } + + } else if ((num & FILE_ATTRIBUTE_DIRECTORY) != (rv & FILE_ATTRIBUTE_DIRECTORY)){ + // ファイルの移動先にフォルダ、またはフォルダの移動先にファイルが存在するなら + move_away_file(dest_path); // それをどかす + if (MoveFile(sorc_path, dest_path) == 0) + return 5; // 移動失敗 + if (num & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性を付け直す + rv = GetFileAttributes(dest_path); + SetFileAttributes(dest_path, rv | FILE_ATTRIBUTE_READONLY); // 失敗してもエラーにしない + } + + } else { // 既にファイルが存在するなら + wchar_t back_path[MAX_LEN]; + + back_path[0] = 0; + if (switch_b & 3){ // 既存のファイルを削除せずに別名で残す + for (num = 1; num < 100; num++){ + swprintf(back_path, _countof(back_path), L"%s.%d", dest_path, num); // バックアップ・ファイルのパス + if (GetFileAttributes(back_path) == INVALID_FILE_ATTRIBUTES) + break; // 存在しないなら OK + } + if (num >= 100) + back_path[0] = 0; // バックアップがいっぱいならそれ以上作らない + } + if (back_path[0] != 0){ // バックアップを作って置き換える + //printf_cp("back_path = %s\n", back_path); + num = ReplaceFile(dest_path, sorc_path, back_path, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0); + while (num == 0){ + num = GetLastError(); + //printf("error = %d\n", num); + // The process cannot access the file because it is being used by another process. + if ((num == 32) || // ERROR_SHARING_VIOLATION + (num == 33) || // ERROR_LOCK_VIOLATION + (num == 1175) || // ERROR_UNABLE_TO_REMOVE_REPLACED + (num == 1176)){ // ERROR_UNABLE_TO_MOVE_REPLACEMENT + Sleep(FILE_WAIT_SEC); + num = ReplaceFile(dest_path, sorc_path, back_path, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0); + } else if (num == 1177){ // ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 + Sleep(FILE_WAIT_SEC); + num = MoveFile(sorc_path, dest_path); // 既存ファイルはバックアップ済みなら、移動するだけ + } else { + return 6; + } + retry_time--; + if (retry_time == 0) + break; + } + if (num == 0) + return 7; + if (switch_b & 2) // バックアップをゴミ箱に入れる + delete_file_recycle(back_path); + } else { // 既存のファイルを削除して置き換える + if (rv & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性なら、一旦その属性を解除する + if (SetFileAttributes(dest_path, rv ^ FILE_ATTRIBUTE_READONLY) == 0) + return 7; // 移動先の読み取り専用属性を解除できなかった = 削除もできない + } + num = ReplaceFile(dest_path, sorc_path, NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0); + while (num == 0){ + num = GetLastError(); + //printf("error = %d\n", num); + if ((num == 32) || // ERROR_SHARING_VIOLATION + (num == 33) || // ERROR_LOCK_VIOLATION + (num == 1175)){ // ERROR_UNABLE_TO_REMOVE_REPLACED + Sleep(FILE_WAIT_SEC); + num = ReplaceFile(dest_path, sorc_path, NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0); + } else if (num == 1176){ // ERROR_UNABLE_TO_MOVE_REPLACEMENT + Sleep(FILE_WAIT_SEC); + num = MoveFile(sorc_path, dest_path); // 既存ファイルは削除済みなら、移動するだけ + } else { + return 8; + } + retry_time--; + if (retry_time == 0) + break; + } + if (num == 0) + return 9; + if (rv & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性を付け直す + rv = GetFileAttributes(dest_path); + SetFileAttributes(dest_path, rv | FILE_ATTRIBUTE_READONLY); // 失敗してもエラーにしない + } + } + } + + return 0; +} + +// ファイルまたはフォルダをどかす +void move_away_file( + wchar_t *file_path) // ファイル・パス +{ + wchar_t new_path[MAX_LEN]; + unsigned int rv, num; + + rv = GetFileAttributes(file_path); + if (rv != INVALID_FILE_ATTRIBUTES){ // 既にファイルが存在するなら + if (rv & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性なら、一旦その属性を解除する + if (SetFileAttributes(file_path, rv ^ FILE_ATTRIBUTE_READONLY) == 0) + return; // 既存ファイルの読み取り専用属性を解除できなかった + } + + if (switch_b & 3){ // 削除せずに別名で残す + for (num = 1; num < 100; num++){ + swprintf(new_path, _countof(new_path), L"%s.%d", file_path, num); + if (MoveFile(file_path, new_path) != 0){ + break; // 成功したらループから抜ける + } else if (GetLastError() != ERROR_ALREADY_EXISTS){ + break; // Destination file already exists (183) で移動できない時だけ再試行する + } + } + if (num < 100){ // 移動できた + if (rv & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性を付け直す + rv = GetFileAttributes(new_path); + SetFileAttributes(new_path, rv | FILE_ATTRIBUTE_READONLY); // 失敗してもエラーにしない + } + if (switch_b & 2) // ゴミ箱に入れる + delete_file_recycle(new_path); + return; + } + } + + // 削除する + if (rv & FILE_ATTRIBUTE_DIRECTORY){ + RemoveDirectory(file_path); // フォルダを削除する + } else { + DeleteFile(file_path); // 削除する + } + } +} + +// ファイルを指定サイズに縮小する +int shorten_file( + wchar_t *file_path, // ファイル・パス + __int64 new_size) +{ + wchar_t back_path[MAX_LEN]; + unsigned int rv, len, num; + HANDLE hFileWrite; + LARGE_INTEGER qwi; // Quad Word Integer + + back_path[0] = 0; + if (switch_b & 3){ // 既存のファイルを削除せずに別名で残す + for (num = 1; num < 100; num++){ + swprintf(back_path, _countof(back_path), L"%s.%d", file_path, num); // バックアップ・ファイルのパス + if (GetFileAttributes(back_path) == INVALID_FILE_ATTRIBUTES) + break; // 存在しないなら OK + } + if (num >= 100) + back_path[0] = 0; // バックアップがいっぱいならそれ以上作らない + } + + if (back_path[0] != 0){ // バックアップを作って置き換える + wchar_t temp_path[MAX_LEN]; + // テンポラリー・ファイルを作ってファイル内容をコピーする + get_temp_name(file_path, temp_path); + hFileWrite = CreateFile(temp_path, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFileWrite == INVALID_HANDLE_VALUE){ // 作れなかったら + back_path[0] = 0; + } else if (new_size <= 0){ + CloseHandle(hFileWrite); + } else { + // ファイル・サイズを指定サイズにする + qwi.QuadPart = new_size; + if (!SetFilePointerEx(hFileWrite, qwi, NULL, FILE_BEGIN)){ + back_path[0] = 0; + } else if (!SetEndOfFile(hFileWrite)){ + back_path[0] = 0; + } else { + // ポインターを先頭に戻す + qwi.QuadPart = 0; + if (!SetFilePointerEx(hFileWrite, qwi, NULL, FILE_BEGIN)){ + back_path[0] = 0; + } else { + __int64 left_size = new_size; + HANDLE hFileRead; + hFileRead = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFileRead == INVALID_HANDLE_VALUE){ + back_path[0] = 0; + } else { // 指定サイズまでコピーする + unsigned char buf[IO_SIZE]; + while (left_size > 0){ + len = IO_SIZE; + if (left_size < IO_SIZE) + len = (unsigned int)left_size; + left_size -= len; + if (!ReadFile(hFileRead, buf, len, &rv, NULL) || (len != rv)){ + back_path[0] = 0; + break; + } + if (!WriteFile(hFileWrite, buf, len, &rv, NULL)){ + back_path[0] = 0; + break; + } + } + CloseHandle(hFileRead); + } + } + } + CloseHandle(hFileWrite); + if (back_path[0] == 0) // 失敗したらテンポラリー・ファイルは削除する + DeleteFile(temp_path); + } + + if (back_path[0] != 0){ // バックアップを作って置き換える + if (ReplaceFile(file_path, temp_path, back_path, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0) == 0){ + DeleteFile(temp_path); + return 1; + } + if (switch_b & 2) // バックアップをゴミ箱に入れる + delete_file_recycle(back_path); + return 0; + } + } + + num = GetFileAttributes(file_path); + if (num & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性なら、一旦その属性を解除する + if (SetFileAttributes(file_path, num ^ FILE_ATTRIBUTE_READONLY) == 0) + return 1; // 読み取り専用属性を解除できなかった = 縮小もできない + } + + // 直接開いて縮小する + hFileWrite = CreateFile(file_path, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hFileWrite == INVALID_HANDLE_VALUE) + return 1; + // ファイル・サイズを指定サイズにする + qwi.QuadPart = new_size; + if (!SetFilePointerEx(hFileWrite, qwi, NULL, FILE_BEGIN)){ + CloseHandle(hFileWrite); + return 1; + } + if (!SetEndOfFile(hFileWrite)){ + CloseHandle(hFileWrite); + return 1; + } + CloseHandle(hFileWrite); + + if (num & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性を付け直す + num = GetFileAttributes(file_path); + SetFileAttributes(file_path, num | FILE_ATTRIBUTE_READONLY); // 失敗してもエラーにしない + } + + return 0; +} + +// デバイス名かどうかを判定する +static int check_device_name(wchar_t *name, int len) +{ + if (len >= 3){ + if ((name[3] == 0) || (name[3] == '.') || (name[3] == '\\')){ + if (_wcsnicmp(name, L"CON", 3) == 0) + return 1; + if (_wcsnicmp(name, L"PRN", 3) == 0) + return 1; + if (_wcsnicmp(name, L"AUX", 3) == 0) + return 1; + if (_wcsnicmp(name, L"NUL", 3) == 0) + return 1; + } + if (len >= 4){ + if ((name[4] == 0) || (name[4] == '.') || (name[4] == '\\')){ + if (_wcsnicmp(name, L"COM", 3) == 0){ + if ((name[3] >= 0x31) && (name[3] <= 0x39)) + return 1; + } + if (_wcsnicmp(name, L"LPT", 3) == 0){ + if ((name[3] >= 0x31) && (name[3] <= 0x39)) + return 1; + } + } + } + } + + return 0; +} + +// ファイル名が有効かどうか調べる(ディレクトリ記号は警告しない) +// 戻り値 0=変更無し, +2=浄化される, +4=文字数が変わる, +8=警告 +int check_filename( + wchar_t *name) // 検査するファイル名 +{ + int i, len = 0; + + // 制御文字 1~31 (改行やタブなど) を削除する + while (len < MAX_LEN){ + if (name[len] == 0){ + break; + } else if (name[len] < 32){ + return 4; + } else { + len++; + } + } + if (len == 0) + return 4; + + // WinOS ではファイル名に「:*?"<>|」の文字は使えない + for (i = 0; i < len; i++){ + if ((name[i] == ':') || (name[i] == '*') || (name[i] == '?') || (name[i] == '"') + || (name[i] == '<') || (name[i] == '>') || (name[i] == '|')){ + return 2; // 「\,:,*,?,",<,>,|」 + } + } + + // 先頭の「 」、末尾の「.」「 」を警告する + if ((name[0] == ' ') || (name[len - 1] == '.') || (name[len - 1] == ' ')) + return 8; + + // デバイス名は警告する + if (check_device_name(name, len) != 0) + return 8; + + return 0; +} + +// ファイル名が有効か確かめて、問題があれば浄化する +// 戻り値 0=変更無し, +1=ディレクトリ記号, +2=浄化した, +4=文字数が変わった, +8=警告, 16=エラー +int sanitize_filename( + wchar_t *name, // 検査するファイル名 + file_ctx_r *files, // 各ソース・ファイルの情報 + int num) // 比較から除外するファイル番号 +{ + int i, j, rv = 0, len = 0; + + // 制御文字 1~31 (改行やタブなど) を削除する + while (len < MAX_LEN){ + if (name[len] == 0){ + break; + } else if (name[len] < 32){ + i = len; + do { + name[i] = name[i + 1]; + i++; + } while (name[i] != 0); + rv |= 4; + } else { + len++; + } + } + if (len == 0) + name[0] = 0; + + // WinOS ではファイル名に「:*?"<>|」の文字は使えないので置換する + for (i = 0; i < len; i++){ + if ((name[i] == ':') || (name[i] == '*') || (name[i] == '?') || (name[i] == '"') + || (name[i] == '<') || (name[i] == '>') || (name[i] == '|')){ + name[i] = '_'; // 「: * ? " < > |」 ->「_」 + rv |= 2; + } else if (name[i] == '/'){ // ディレクトリ記号を変換する、元から「\」ならそのまま + name[i] = '\\'; // 「/」 -> 「\」 + rv |= 1; + } + } + + // 先頭の「 」、末尾の「.」「 」を警告する + if ((name[0] == ' ') || (name[len - 1] == '.') || (name[len - 1] == ' ')) + rv |= 8; + // 「\」の前後に「 」があれば警告する + i = len - 1; + while (i > 0){ + if ((name[i] == '\\') && ((name[i - 1] == ' ') || (name[i + 1] == ' '))) + rv |= 8; + i--; + } + + // 「\」の前に「.」があれば削除する (ディレクトリ移動を防ぐ) + i = len - 1; + while (i > 0){ + if ((name[i] == '\\') && (name[i - 1] == '.')){ + for (j = i - 1; j < len; j++) + name[j] = name[j + 1]; + len--; + rv |= 4; + } + i--; + } + + // 連続した「\」を一文字にする + i = 0; + while (i < len){ + if ((name[i] == '\\') && (name[i + 1] == '\\')){ + for (j = i + 1; j < len; j++) + name[j] = name[j + 1]; + len--; + rv |= 4; + } else { + i++; + } + } + + // 先頭の「\」を削除する + if (name[0] == '\\'){ + for (i = 0; i < len; i++) + name[i] = name[i + 1]; + len--; + rv |= 4; + } + + // デバイス名を警告する (サブ・ディレクトリも含める) + i = len; + while (i > 0){ + i--; + if ((i == 0) || ((i > 0) && (name[i - 1] == '\\'))){ + if (check_device_name(name + i, len) != 0) + rv |= 8; + } + } + + // 既に同名のファイルが存在するなら、末尾に「(値)」を追加する + if (files != NULL){ + int k; + if (len == 0){ // ファイル名が空になった場合は必ず追加する + i = 0; + } else { + // 大文字と小文字を区別するシステム上で作られた PAR ファイルは Windows OS では問題になる + for (i = 0; i < file_num; i++){ + if (i == num){ + if (rv & 6){ + continue; // 元のファイル名とは比較しない + } else { + i = file_num; + break; // それまでのファイル名とだけ比較する + } + } + if (files[i].name <= 0) + continue; // ファイル名が取得できなかったファイルとは比較しない + if (_wcsicmp(name, list_buf + files[i].name) == 0) + break; // ファイル名が同じ + } + } + if (i < file_num){ + if (len >= MAX_LEN - 3) + return 16; + k = 0; + for (j = len - 1; j > 0; j--){ + if (name[j] == '.'){ + k = j; + break; + } + if (name[j] == '\\'){ // サブ・ディレクトリは除外する + if (j == len - 1) + k = len - 1; // フォルダなら + break; + } + } + if (k > 0){ // 拡張子があるなら、拡張子の前に追加する + for (j = len; j >= k; j--) + name[j + 3] = name[j]; + name[k ] = '('; + name[k + 2] = ')'; + k = k + 1; + } else { // 拡張子が無ければ末尾に追加する + name[len ] = '('; + name[len + 2] = ')'; + name[len + 3] = 0; + k = len + 1; + } + len += 3; + rv |= 4; + for (j = 1; j < 10; j++){ + name[k] = (unsigned short)(0x30 + j); + for (i = 0; i < file_num; i++){ + if (i == num){ + if (rv & 6){ + continue; // 元のファイル名とは比較しない + } else { + i = file_num; + break; // それまでのファイル名とだけ比較する + } + } + if (files[i].name <= 0) + continue; // ファイル名が取得できなかったファイルとは比較しない + if (_wcsicmp(name, list_buf + files[i].name) == 0) + break; // ファイル名が同じ + } + if (i == file_num) + break; + } + if (j >= 10) + return 16; // 同名になるファイルが多過ぎる + } + } + + return rv; +} + +// リカバリ・ファイルのパスから拡張子とボリューム番号を取り除く +void get_base_filename( + wchar_t *file_path, // リカバリ・ファイルのパス + wchar_t *base_path, // 基準ファイル名のパス + wchar_t *file_ext) // 拡張子 (存在するなら) +{ + wchar_t *tmp_p, *file_name; + int len, match; + + file_ext[0] = 0; + wcscpy(base_path, file_path); + file_name = offset_file_name(base_path); + tmp_p = wcsrchr(file_name, '.'); + if (tmp_p != NULL){ // 拡張子または「.vol*」が存在すれば取り除く + // 拡張子が無い場合も考慮する + if (_wcsnicmp(tmp_p, L".vol", 4) == 0){ + match = 0; + len = 4; + while (tmp_p[len] != 0){ + if ((tmp_p[len] >= 48) && (tmp_p[len] <= 57)){ + match |= 1; // 数字は何個あってもよい + } else if ((tmp_p[len] == '+') || (tmp_p[len] == '-') || (tmp_p[len] == '_')){ + match += 2; // 記号は一個だけしかだめ + } else { // それ以外なら判定終了 + break; + } + len++; + } + if (match == 3) + tmp_p[0] = 0; // 取り除く + } + if (tmp_p[0] != 0){ // 拡張子があるなら + if (wcslen(tmp_p) < EXT_LEN){ + wcscpy(file_ext, tmp_p); // 拡張子を記録しておく + tmp_p[0] = 0; // 拡張子を取り除く + } + } + // 更に「.vol*」があればそれも取り除く + tmp_p = wcsrchr(file_name, '.'); + if (tmp_p != NULL){ + if (_wcsnicmp(tmp_p, L".vol", 4) == 0){ + match = 0; + len = 4; + while (tmp_p[len] != 0){ + if ((tmp_p[len] >= 48) && (tmp_p[len] <= 57)){ + match |= 1; // 数字は何個あってもよい + } else if ((tmp_p[len] == '+') || (tmp_p[len] == '-') || (tmp_p[len] == '_')){ + match += 2; // 記号は一個だけしかだめ + } else { // それ以外なら判定終了 + break; + } + len++; + } + if (match == 3) + tmp_p[0] = 0; // 取り除く + } + } + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* +// バイトごとに16進数表示にする、十分な領域を確保しておくこと +void byte_encode(wchar_t *out, unsigned char *in, int len) +{ + int i, c; + + for (i = 0; i < len; i++){ + c = in[i] >> 4; + if (c < 10){ // 0 to 9 + *out = c + 48; + } else { // A to Z + *out = c - 10 + 65; + } + out++; + c = in[i] & 15; + if (c < 10){ // 0 to 9 + *out = c + 48; + } else { // A to Z + *out = c - 10 + 65; + } + out++; + } + *out = 0; +} + +void byte_decode(unsigned char *out, wchar_t *in, int len) +{ + unsigned int c1, c2; + + while (len >= 2){ + c1 = *in; + in++; + if ((c1 >= 48) && (c1 <= 57)){ // 0 to 9 + c1 -= 48; + } else if ((c1 >= 65) && (c1 <= 90)){ // A to Z + c1 -= 65 - 10; + } else { + break; + } + c2 = *in; + in++; + if ((c2 >= 48) && (c2 <= 57)){ // 0 to 9 + c2 -= 48; + } else if ((c2 >= 65) && (c2 <= 90)){ // A to Z + c2 -= 65 - 10; + } else { + break; + } + *out = (unsigned char)((c1 << 4) | c2); + out++; + len -= 2; + } +} +*/ + +// ハッシュ値を表示する +void print_hash(unsigned char hash[16]) +{ + printf("%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], + hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]); +} + +// 64-bit 整数の平方根を求める +// by n-trino, http://d.hatena.ne.jp/n-trino/20080225 +unsigned int sqrt64(__int64 num) +{ + unsigned int i, result = 0; + __int64 rest = 0, sum = 0; + + if (num <= 1) + return 1; // 1より小さい値が入力されることは無い + + for (i = 0; i < 32; i++){ + rest = ( rest << 2 ) | ( ( num >> 62 ) & 3 ); + num <<= 2; + result <<= 1; + sum <<= 1; + if ( ( sum | 1 ) <= rest ){ + sum |= 1; + rest -= sum; + sum++; + result |= 1; + } + } + + return result; +} + +int sqrt32(int num) +{ + int i, result = 0, rest = 0, sum = 0; + + if (num <= 1) + return 1; // 1より小さい値が入力されることは無い + + for (i = 0; i < 16; i++){ + rest = ( rest << 2 ) | ( ( num >> 30 ) & 3 ); + num <<= 2; + result <<= 1; + sum <<= 1; + if ( ( sum | 1 ) <= rest ){ + sum |= 1; + rest -= sum; + sum++; + result |= 1; + } + } + + return result; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int cpu_num = 1; // CPU/Core 個数が制限されてる場合は、上位に本来の数を置く +// /arch:SSE2, +1=SSSE3, +2=SSE4.1, +4=SSE4.2, +8=CLMUL, +16=AVX2, +128=JIT(SSE2), +256=Old +unsigned int cpu_flag = 0; +unsigned int cpu_cache = 0; // 上位 16-bit = L2 cache * 2, 下位 16-bit = L3 cache +unsigned int memory_use = 0; // メモリー使用量 0=auto, 1~7 -> 1/8 ~ 7/8 + +static int count_bit(DWORD_PTR value) +{ + int bitCount = 0; + + // Shift を使わない方法、計算量は平均 (n/2) + while (value != 0){ + bitCount++; + value &= (value - 1); + } + + return bitCount; +} + +void check_cpu(void) +{ + int core_count = 0, use_count; + unsigned int CPUInfo[4]; + unsigned int returnLength, byteOffset; + DWORD_PTR ProcessAffinityMask, SystemAffinityMask; // 32-bit なら 4バイト、64-bit なら 8バイト整数 + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL, ptr; + + // CPU の拡張機能を調べる + __cpuid(CPUInfo, 1); + cpu_flag |= (CPUInfo[3] & (1 << 26)) >> 19; // SSE2 対応か + cpu_flag |= (CPUInfo[2] & (1 << 9)) >> 9; // SSSE3 対応か + cpu_flag |= (CPUInfo[2] & (1 << 19)) >> 18; // SSE4.1 対応か + cpu_flag |= (CPUInfo[2] & (1 << 20)) >> 18; // SSE4.2 対応か + cpu_flag |= (CPUInfo[2] & (1 << 1)) << 2; // CLMUL 対応か + if ((CPUInfo[2] & (1 << 28)) != 0){ // AVX 対応なら + if (IsWindows7OrGreater()){ // Windows 7 以降なら AVX2 の判定をする + __cpuid(CPUInfo, 0); + if (CPUInfo[0] >= 7){ // AVX2 用の基本命令領域があるなら + __cpuidex(CPUInfo, 7, 0); + cpu_flag |= (CPUInfo[1] & (1 << 5)) >> 1; // AVX2 対応か + } + } + } + + // 使用可能なコア個数とキャッシュ性能を調べる + if (GetProcessAffinityMask(GetCurrentProcess(), &ProcessAffinityMask, &SystemAffinityMask) != 0){ + cpu_num = count_bit(ProcessAffinityMask); + //printf("ProcessAffinityMask = 0x%08Ix, available logical processor cores = %d\n", ProcessAffinityMask, cpu_num); + } + if (cpu_num <= 0){ // プロセスが使用可能なコア個数の取得に失敗したら総数を使う + SYSTEM_INFO si; + ProcessAffinityMask = -1; + GetSystemInfo(&si); + cpu_num = si.dwNumberOfProcessors; + //printf("total logical processor cores = %d\n", cpu_num); + } + + // CPU 情報を取得する + returnLength = 0; + if (GetLogicalProcessorInformation(buffer, &returnLength) == FALSE){ + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER){ + buffer = malloc(returnLength); + if (buffer != NULL){ + if (GetLogicalProcessorInformation(buffer, &returnLength) == FALSE){ + free(buffer); + buffer = NULL; + } + } + } + } + + // 項目ごとに解析する + if (buffer != NULL){ + int cache2_size = 0, cache2_way, cache3_size = 0, cache3_way; +/* +実験してもらってからじゃないと、本採用しにくい・・・ + DWORD numaNodeCount = 0, processorPackageCount = 0; + DWORD_PTR numaNodeMask = 0, processorPackageMask = 0; + + byteOffset = 0; + ptr = buffer; + while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength){ + if (ptr->ProcessorMask & ProcessAffinityMask){ + switch (ptr->Relationship){ + case RelationNumaNode: + // Non-NUMA systems report a single record of this type. + numaNodeCount++; + if (count_bit(ptr->ProcessorMask) > count_bit(numaNodeMask)) + numaNodeMask = ptr->ProcessorMask; + break; + case RelationProcessorPackage: + // Logical processors share a physical package. + processorPackageCount++; + if (count_bit(ptr->ProcessorMask) > count_bit(processorPackageMask)) + processorPackageMask = ptr->ProcessorMask; + break; + } + } + byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + ptr++; + } + //printf("Number of NUMA nodes: %d, Mask = 0x%08Ix\n", numaNodeCount, numaNodeMask); + //printf("Number of physical processor packages: %d, Mask = 0x%08Ix\n", processorPackageCount, processorPackageMask); + if (numaNodeCount > 1){ // NUMAノードが複数あるなら + // 使用する Core を一つの NUMAノードのだけに制限する + if (SetProcessAffinityMask(GetCurrentProcess(), numaNodeMask) != 0){ + cpu_num = count_bit(numaNodeMask); + ProcessAffinityMask = numaNodeMask; + //printf("Number of logical processor cores in a NUMA node: %d\n", cpu_num); + } + } +*/ + + byteOffset = 0; + ptr = buffer; + while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength){ + //printf("Relationship = %d, ProcessorMask = 0x%08Ix\n", ptr->Relationship, ptr->ProcessorMask); + if (ptr->ProcessorMask & ProcessAffinityMask){ + switch (ptr->Relationship){ + case RelationProcessorCore: + //printf("ProcessorCore.Flags = %d, ProcessorMask = 0x%08Ix\n", ptr->ProcessorCore.Flags, ptr->ProcessorMask); + if (ptr->ProcessorCore.Flags){ + // Logical processors share functional units, as in Hyperthreading or SMT. + core_count += 1; + } else { + // Logical processors do not share functional units. + core_count += count_bit(ptr->ProcessorMask); + } + break; + case RelationCache: + // Cache data is in ptr->Cache, one CACHE_DESCRIPTOR structure for each cache. + if ((ptr->Cache.Level >= 2) && (ptr->Cache.Associativity > 0) && ((ptr->Cache.Type & 1) == 0)){ + //printf("Cache: Level = %d, Size = %d KB, Associativity = %d, Type = %d, Mask = 0x%08Ix\n", ptr->Cache.Level, ptr->Cache.Size >> 10, ptr->Cache.Associativity, ptr->Cache.Type, ptr->ProcessorMask); + if ((ptr->Cache.Level == 2) && (cache2_size == 0)){ + if (ptr->Cache.Associativity == 0xFF){ + cache2_way = 4; + } else { + cache2_way = ptr->Cache.Associativity; + } + cache2_size = ptr->Cache.Size; + } else if ((ptr->Cache.Level == 3) && (cache3_size == 0)){ + if (ptr->Cache.Associativity == 0xFF){ + cache3_way = 4; + } else { + cache3_way = ptr->Cache.Associativity; + } + cache3_size = ptr->Cache.Size; + } + } + break; + } + } + byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + ptr++; + } + free(buffer); + //printf("Number of available physical processor cores: %d\n", core_count); + if (cache3_size > 0){ + //printf("L3 cache: %d KB (%d way)\n", cache3_size >> 10 , cache3_way); + cache3_size /= cache3_way; // set-associative のサイズにする + if (cache3_size < 131072) + cache3_size = 128 << 10; // 128 KB 以上にする + } + if (cache2_size > 0){ + //printf("L2 cache: %d KB (%d way)\n", cache2_size >> 10, cache2_way); + cache2_size /= cache2_way; // set-associative のサイズにする + if (cache2_size < 32768) + cache2_size = 32 << 10; // 32 KB 以上にする + //printf("Limit size of Cache Blocking: %d KB\n", cache2_size >> 10); + cpu_cache = cache2_size | (cache3_size >> 17); + } + } + + if (cpu_cache == 0) // キャッシュ・サイズが不明なら、128 KB にする + cpu_cache = 128 << 10; + if (core_count == 0){ // 物理コア数が不明なら、論理コア数と同じにする + core_count = cpu_num; + use_count = cpu_num; + } else if (core_count < cpu_num){ // 物理コア数が共有されてるなら + if (core_count >= 6){ // 6 コア以上ならそれ以上増やさない + use_count = core_count; + } else { // 2~5 コアなら 1個だけ増やす + use_count = core_count + 1; + } + } else { + use_count = core_count; + } + if (use_count > MAX_CPU) // 利用するコア数が実装上の制限を越えないようにする + use_count = MAX_CPU; + //printf("Core count: logical, physical, use = %d, %d, %d\n", cpu_num, core_count, use_count); + // 上位に論理コア数と物理コア数、下位に利用するコア数を配置する + cpu_num = (cpu_num << 24) | (core_count << 16) | use_count; + + // cpu_flag の上位 17-bit にキャッシュの制限サイズを置く + cpu_flag |= cpu_cache & 0xFFFF8000; // 32 KB 未満は無視する +} + +// OS が 32-bit か 64-bit かを調べる +int check_OS64(void) +{ + BOOL bWow64Process = FALSE; + + IsWow64Process(GetCurrentProcess(), &bWow64Process); + + return bWow64Process; +} + +// 空きメモリー量と制限値から使用できるメモリー量を計算する +size_t get_mem_size(size_t trial_alloc) +{ + size_t mem_size; + MEMORYSTATUSEX statex; + + statex.dwLength = sizeof(statex); + if (GlobalMemoryStatusEx(&statex)){ + unsigned __int64 mem_size64, cache_size; +/* + printf("MemoryLoad = %d\n", statex.dwMemoryLoad); + printf("TotalPhys = %10I64d\n", statex.ullTotalPhys); + printf("AvailPhys = %10I64d\n", statex.ullAvailPhys); + printf("TotalPageFile = %10I64d\n", statex.ullTotalPageFile); + printf("AvailPageFile = %10I64d\n", statex.ullAvailPageFile); + printf("TotalVirtual = %10I64d\n", statex.ullTotalVirtual); + printf("AvailVirtual = %10I64d\n", statex.ullAvailVirtual); +*/ + cache_size = statex.ullAvailPhys >> 3; // available size / 8 + if (memory_use & 7){ // manual setting (1/8 ~ 7/8) + mem_size64 = cache_size * (memory_use & 7); // using size = available size / 8 * memory_use + } else { // auto setting +#ifndef _WIN64 // 32-bit OS なら、OSがメモリーの半分を使うと想定する + // 1 GB = 1024 MB, 1024 / 16 = 64 MB, 512 / 8 = 32 MB + // 2 GB = 2048 MB, 2048 / 16 = 128 MB, 1024 / 8 = 128 MB + // disk cache size is range from 100MB to 200MB. + cache_size += statex.ullTotalPhys >> 4; // cache size = available size / 8 + total size / 16 +#else // 64-bit OS なら、OSがメモリーを 2GB 使うと想定する + // 4 GB = 4096 MB, 4096 / 32 = 128 MB, 2048 / 8 = 256 MB + // 8 GB = 8192 MB, 8192 / 32 = 256 MB, 6144 / 8 = 768 MB + // 16 GB = 16384 MB, 16384 / 32 = 512 MB, 14336 / 8 = 1792 MB + // 32 GB = 32768 MB, 32768 / 32 = 1024 MB, 30720 / 8 = 3840 MB + cache_size += statex.ullTotalPhys >> 5; // cache size = available size / 8 + total size / 32 +#endif + if (cache_size > statex.ullAvailPhys >> 2) + cache_size = statex.ullAvailPhys >> 2; // cache size should be less than 1/4 of available size. + mem_size64 = statex.ullAvailPhys - cache_size; // using size = available size - cache size + } + if (mem_size64 + 0x00200000 > statex.ullAvailVirtual) + mem_size64 = statex.ullAvailVirtual - 0x00200000; // reserve 2MB for other task + +#ifndef _WIN64 // 32-bit 版なら + if (mem_size64 > MAX_MEM_SIZE) // 確保する最大サイズを 2GB までにする + mem_size64 = MAX_MEM_SIZE; + if (check_OS64() == 0){ // 32-bit OS 上なら更に制限する + if (mem_size64 > MAX_MEM_SIZE32) + mem_size64 = MAX_MEM_SIZE32; + } +#endif + mem_size = (size_t)mem_size64 & ~(size_t)0xFFFF; // multiple of 64KB + + } else { + print_win32_err(); + mem_size = 0x20000000; // メモリー量を認識できない場合は 512MB と仮定する + } + + // try to allocate for test + if (trial_alloc){ + unsigned char *tmp_p; + if (mem_size > trial_alloc) // no need to test over than required size + mem_size = trial_alloc; + //printf("\nget_mem_size: try %d MB ...", mem_size >> 20); + tmp_p = malloc(mem_size); + while ((tmp_p == NULL) && (mem_size > 0x08000000)){ // if over than 128MB + mem_size -= (mem_size >> 4); // reduce 1/16 + mem_size &= ~(size_t)0xFFFF; + tmp_p = malloc(mem_size); + } + if (tmp_p != NULL){ + free(tmp_p); + //printf(" %d MB ok\n", mem_size >> 20); + } else { + //printf(" %d MB failed\n", mem_size >> 20); + mem_size = (mem_size / 2) & ~(size_t)0xFFFF; // half size + } + } + + return mem_size; +} + +// 外部関数の定義 com.cpp +//extern int check_msft(void); + +// 記録装置の特性を調べるために、次の記事を参考にした。 +//「SSD なら動作を変えるアプリケーションを作る」by NyaRuRu +//「SSDかどうかをC#から判別する 」by EMO + +// Returns 0 if |physical_drive_path| has no seek penalty. +// Returns 1 otherwise. +// Returns 2~ if fails to retrieve the status. +int check_seek_penalty(wchar_t *dir_path){ // ディレクトリを指定する + wchar_t *drive_letter, logical_drive[8]; + unsigned int return_value, return_size; + HANDLE hDevice; + STORAGE_PROPERTY_QUERY query_property; + DEVICE_SEEK_PENALTY_DESCRIPTOR query_seek_penalty_desc; + DEVICE_TRIM_DESCRIPTOR query_trim_desc; + STORAGE_ADAPTER_DESCRIPTOR query_adapter_desc; + +// if (IsWindows8OrGreater()){ // Windows 8 以降なら +// printf("\n calling check_msft()\n"); +// return_value = check_msft(); +// printf("\ncheck_msft = %d\n\n", return_value); +// } + + // ドライブ文字を取得する + drive_letter = dir_path; + if (wcsncmp(drive_letter, L"\\\\?\\", PREFIX_LEN) == 0) + drive_letter += PREFIX_LEN; + if (drive_letter[1] != ':') + return 2; // 「C:\~」のような形式にだけ対応する + if ((drive_letter[0] >= 0x41) && (drive_letter[0] <= 0x5A)){ // 大文字の A~Z なら + logical_drive[4] = drive_letter[0]; + } else if ((drive_letter[0] >= 0x61) && (drive_letter[0] <= 0x7A)){ // 小文字の a~z なら + logical_drive[4] = drive_letter[0]; + } else { + return 3; // ドライブ文字でないならだめ + } + +/* + // GetDriveType 用に "\\\\.\\C:\" のフォーマットにする + logical_drive[0] = '\\'; + logical_drive[1] = '\\'; + logical_drive[2] = '.'; + logical_drive[3] = '\\'; + logical_drive[5] = ':'; + logical_drive[6] = '\\'; + logical_drive[7] = 0; + return_value = GetDriveType(logical_drive); + //printf("GetDriveType = %d\n", return_value); + if (return_value == 6) // DRIVE_RAMDISK + return 0; // RAM disk は SSD と同じ扱いにする + if (return_value != 3) // DRIVE_FIXED 以外は駄目 + return 1; +*/ + + // "\\\\.\\C:" のフォーマットにする + logical_drive[0] = '\\'; + logical_drive[1] = '\\'; + logical_drive[2] = '.'; + logical_drive[3] = '\\'; + logical_drive[5] = ':'; + logical_drive[6] = 0; + + // We do not need write permission. + hDevice = CreateFile(logical_drive, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (hDevice == INVALID_HANDLE_VALUE) + return 4; + + // アダプター情報を取得する (BusType は USBなら 7、SATAなら 11、NVMeなら 17 らしい) + return_size = 0; + query_property.PropertyId = 1; // StorageAdapterProperty + query_property.QueryType = 0; // PropertyStandardQuery + return_value = DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, + &query_property, sizeof(query_property), + &query_adapter_desc, sizeof(query_adapter_desc), + &return_size, NULL); + if ((return_value != 0) && (query_adapter_desc.Size == sizeof(query_adapter_desc))){ +/* printf("\n STORAGE_ADAPTER_DESCRIPTOR\n Version = %d, Size = %d, BusType = %d\n", + query_adapter_desc.Version, + query_adapter_desc.Size, + query_adapter_desc.BusType);*/ + if (query_adapter_desc.BusType == 17) // BusType 17 = NVMe + memory_use |= 32; + } + + // SSD なら seek penalty よりも TRIM コマンドを調べたほうが確実らしい + // https://stackoverflow.com/questions/23363115/detecting-ssd-in-windows + return_size = 0; + query_property.PropertyId = 8; // StorageDeviceTrimProperty + query_property.QueryType = 0; // PropertyStandardQuery + return_value = DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, + &query_property, sizeof(query_property), + &query_trim_desc, sizeof(query_trim_desc), + &return_size, NULL); + if ((return_value != 0) && (query_trim_desc.Size == sizeof(query_trim_desc))){ +/* printf("\n DEVICE_TRIM_DESCRIPTOR\n Version = %d, Size = %d, TrimEnabled = %d\n", + query_trim_desc.Version, + query_trim_desc.Size, + query_trim_desc.TrimEnabled);*/ + if (query_trim_desc.TrimEnabled != 0){ // TRIM command available = maybe SSD drive + memory_use |= 16; + CloseHandle(hDevice); + return 0; + } + } + + // seek penalty なら RAM disk も判定できるかも? + return_size = 0; + query_property.PropertyId = 7; // StorageDeviceSeekPenaltyProperty + query_property.QueryType = 0; // PropertyStandardQuery + return_value = DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, + &query_property, sizeof(query_property), + &query_seek_penalty_desc, sizeof(query_seek_penalty_desc), + &return_size, NULL); + if ((return_value != 0) && (query_seek_penalty_desc.Size == sizeof(query_seek_penalty_desc))){ +/* printf("\n DEVICE_SEEK_PENALTY_DESCRIPTOR\n Version = %d, Size = %d, IncursSeekPenalty = %d\n", + query_seek_penalty_desc.Version, + query_seek_penalty_desc.Size, + query_seek_penalty_desc.IncursSeekPenalty);*/ + if (query_seek_penalty_desc.IncursSeekPenalty == 0){ // no seek penalty = maybe SSD drive + memory_use |= 16; + CloseHandle(hDevice); + return 0; + } + } + + CloseHandle(hDevice); + return 1; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define MAX_NAME_LEN 69 // 経過表示のタイトルの最大文字数 (末尾の null 文字を含む) + +int prog_last; // 前回と同じ進捗状況は出力しないので記録しておく +int count_last; + +// ファイル・パスを短縮されたファイル名だけにしてコピーする +// ASCII 文字は 1文字, それ以外は 2文字として数えることに注意! +static void copy_filename(wchar_t *out, wchar_t *in) +{ + int i, len, name_off, ext_off, ext_len; + + // ファイル名部分の文字数を数える + name_off = 0; + ext_off = 0; + len = 0; + for (i = 0; in[i] != 0; i++){ + if ((in[i] == '\\') && (in[i + 1] != 0)){ + len = 0; + name_off = i + 1; + } else { + if (in[i] < 0x80){ + len += 1; + if ((in[i] == '.') && (in[i + 1] != 0)) + ext_off = i; + } else { + len += 2; + ext_off = 0; // 拡張子は ASCII文字だけにする + } + } + } + if (len < MAX_NAME_LEN){ + wcscpy(out, in + name_off); + return; + } + + // 拡張子の長さを調べる + ext_len = 0; + if (ext_off > name_off){ + while (in[ext_off + ext_len] != 0) + ext_len++; + if (ext_len >= EXT_LEN) + ext_len = 0; + } + + // ファイル名が長すぎる場合は、末尾を省いて拡張子を追加する + len = 0; + for (i = 0; in[name_off + i] != 0; i++){ + if (in[name_off + i] < 0x80){ + if (len >= MAX_NAME_LEN - 2 - ext_len) + break; + len += 1; + } else { + if (len >= MAX_NAME_LEN - 3 - ext_len) + break; + len += 2; + } + out[i] = in[name_off + i]; + } + out[i++] = '~'; + while (ext_len > 0){ + out[i++] = in[ext_off++]; + ext_len--; + } + out[i] = 0; +} + +// 経過のパーセントを表示する +// 普段は 0 を返す、キャンセル時は 0以外 +int print_progress(int prog_now) // 表示する % 値 +{ + if (prog_now < 0) // 範囲外なら + return 0; + + if (_kbhit()){ // キー入力があるか + int ch = _getch(); + if ((ch == 'c') || (ch == 'C')){ // Cancel + printf("\nCancel\n"); + return 2; + } + + // 一時停止と再開の処理 + if ((ch == 'p') || (ch == 'P')){ // Pause + printf(" Pause\r"); // パーセントを上書きする + do { + ch = _getch(); // 再度入力があるまで待つ、CPU 占有率 0% + if ((ch == 'c') || (ch == 'C')){ // 停止中でもキャンセルは受け付ける + printf("\nCancel\n"); + return 2; + } + } while ((ch != 'r') && (ch != 'R')); // Resume + prog_last = -1; // Pause の文字を上書きする + } + } + + // 前回と同じ進捗状況は出力しない + if (prog_now == prog_last) + return 0; + printf("%3d.%d%%\r", prog_now / 10, prog_now % 10); + prog_last = prog_now; + fflush(stdout); + + return 0; +} + +// 経過のパーセントとテキストを表示する +void print_progress_text(int prog_now, char *text) +{ + if (prog_now < 0) // 範囲外なら + return; + printf("%3d.%d%% : %s\r", prog_now / 10, prog_now % 10, text); + prog_last = prog_now; + fflush(stdout); +} + +// 経過のパーセントや個数やファイル名を表示する +// 個数はマイナスなら表示しない +int print_progress_file(int prog_now, int count_now, wchar_t *file_name) +{ + if ((count_now >= 0) && (count_now != count_last)){ + printf("%d \r", count_now); // 個数を表示する + count_last = count_now; + if (prog_now == prog_last) + prog_last = prog_now + 1; // 個数の文字を上書きする + } + + if (prog_now < 0) // 範囲外なら + return 0; + + if (_kbhit()){ // キー入力があるか + int ch = _getch(); + if ((ch == 'c') || (ch == 'C')){ // Cancel + if (prog_last >= 0) + printf("\n"); + printf("Cancel\n"); + return 2; + } + + // 一時停止と再開の処理 + if ((ch == 'p') || (ch == 'P')){ // Pause + printf(" Pause\r"); // パーセントを上書きする + do { + ch = _getch(); // 再度入力があるまで待つ、CPU 占有率 0% + if ((ch == 'c') || (ch == 'C')){ // 停止中でもキャンセルは受け付ける + printf("\nCancel\n"); + return 2; + } + } while ((ch != 'r') && (ch != 'R')); // Resume + if (prog_now == prog_last) + prog_last = prog_now + 1; // Pause の文字を上書きする + } + } + + if (prog_last < 0){ // 初めて経過を表示する時だけファイル名を表示する + char text[MAX_NAME_LEN * 3]; + wchar_t short_name[MAX_NAME_LEN]; + + copy_filename(short_name, file_name); + utf16_to_cp(short_name, text, cp_output); + printf("%3d.%d%% : \"%s\"\r", prog_now / 10, prog_now % 10, text); + } else if (prog_now == prog_last){ // 前回と同じ進捗状況は出力しない + return 0; + } else { + printf("%3d.%d%%\r", prog_now / 10, prog_now % 10); + } + prog_last = prog_now; + fflush(stdout); + + return 0; +} + +void print_progress_done(void) // 終了と改行を表示する +{ + if (prog_last >= 0){ // そもそも経過表示がなかった場合は表示しない + if (prog_last != 1000){ + printf("100.0%%\n"); + } else { + printf("\n"); + } + fflush(stdout); + prog_last = -1; // 進捗状況をリセットする + } +} + +// キャンセルと一時停止を行う +int cancel_progress(void) +{ + if (_kbhit()){ // キー入力があるか + int ch = _getch(); + if ((ch == 'c') || (ch == 'C')){ // Cancel + printf("Cancel\n"); + return 2; + } + + // 一時停止と再開の処理 + if ((ch == 'p') || (ch == 'P')){ // Pause + printf(" Pause\r"); + do { + ch = _getch(); // 再度入力があるまで待つ、CPU 占有率 0% + if ((ch == 'c') || (ch == 'C')){ // 停止中でもキャンセルは受け付ける + printf("Cancel\n"); + return 2; + } + } while ((ch != 'r') && (ch != 'R')); // Resume + } + } + + return 0; +} + +// エラー発生時にキャンセルできるようにする +int error_progress(int error_now, int error_last) +{ + if (error_now != error_last) + printf("Error:%d\n", error_now); + + if (_kbhit()){ // キー入力があるか + int ch = _getch(); + if ((ch == 'c') || (ch == 'C')){ // Cancel + printf("Cancel\n"); + return 2; + } + + // 一時停止と再開の処理 + if ((ch == 'p') || (ch == 'P')){ // Pause + printf(" Pause\r"); + do { + ch = _getch(); // 再度入力があるまで待つ、CPU 占有率 0% + if ((ch == 'c') || (ch == 'C')){ // 停止中でもキャンセルは受け付ける + printf("Cancel\n"); + return 2; + } + } while ((ch != 'r') && (ch != 'R')); // Resume + + // 再会した時には現在の進行状況を表示する + printf("%3d.%d%%\r", prog_last / 10, prog_last % 10); + } + } + + Sleep(UPDATE_TIME); // 一定時間待つ +/* + int ch; + + printf("Press R key to retry, or C key to cancel.\nError\r"); + do { + ch = _getch(); // キー入力があるまで待つ、CPU 占有率 0% + if ((ch == 'c') || (ch == 'C')){ // 停止中でもキャンセルは受け付ける + printf("Cancel\n"); + return 2; + } + } while ((ch != 'r') && (ch != 'R')); // Resume +*/ + + return 0; +} + +// Win32 API のエラー・メッセージを表示する +void print_win32_err(void) +{ + unsigned int en; + LPVOID lpMsgBuf = NULL; + + en = GetLastError(); + if (cp_output == CP_UTF8){ + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, en, 0, (LPWSTR) &lpMsgBuf, 0, NULL) > 0){ + char buf[MAX_LEN * 3]; + // エンコードを UTF-16 から UTF-8 に変換する + utf16_to_utf8(lpMsgBuf, buf); + printf("0x%X, %s\n", en, buf); + } + } else { + if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, en, 0, (LPSTR) &lpMsgBuf, 0, NULL) > 0){ + printf("0x%X, %s\n", en, (char *)lpMsgBuf); + } + } + if (lpMsgBuf != NULL) + LocalFree(lpMsgBuf); +} + +// from com.cpp +extern HRESULT DeleteItem(__in PCWSTR pszSrcItem); + +// ファイルをゴミ箱に移す +int delete_file_recycle(wchar_t *file_path) +{ + wchar_t temp_path[MAX_LEN]; + int rv; + + // "\\?\" が付いてると SHCreateItemFromParsingName が動作しないので取り除く + // パスの文字数が長くても動くみたい + copy_without_prefix(temp_path, file_path); + + rv = DeleteItem(temp_path); + if (rv != S_OK){ + //printf("DeleteItem: error = 0x%08x\n", rv); + if (DeleteFile(file_path) == 0) + return rv; + } + + return 0; + +/* old code for Windows XP + + wchar_t temp_path[MAX_PATH + PREFIX_LEN]; + int rv; + SHFILEOPSTRUCT FileOp; + + // "\\?\" が付いてると ShellAPI が動作しないので取り除く + rv = len_without_prefix(file_path); + if (rv >= MAX_PATH - 1){ // パスの文字数が MAX_PATH - 1 以下でないといけない + // 短いパス名に変換してみる + rv = GetShortPathName(file_path, temp_path, MAX_PATH + PREFIX_LEN); + if ((rv == 0) || (rv >= MAX_PATH + PREFIX_LEN)) + return 1; // パスが長すぎで短縮できない? + // 更にプリフィックスを取り除く + rv = copy_without_prefix(temp_path, temp_path); + //printf("\n short path = \"%S\"\n", temp_path); + } else { + rv = copy_without_prefix(temp_path, file_path); + } + temp_path[rv + 1] = 0; // 末尾にもう一個 null 文字を追加する + + memset(&FileOp, 0, sizeof(FileOp)); + FileOp.wFunc = FO_DELETE; + FileOp.pFrom = temp_path; + FileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT; + rv = SHFileOperation(&FileOp); + if (FileOp.fAnyOperationsAborted == TRUE) + rv = 1; // 削除がキャンセルされた +// if (rv != 0) +// printf("\n SHFileOperation: error = %d\n", rv); + + return rv; +*/ +} + diff --git a/source/par2j/common2.h b/source/par2j/common2.h new file mode 100644 index 0000000..9053adf --- /dev/null +++ b/source/par2j/common2.h @@ -0,0 +1,401 @@ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _WIN64 // 32-bit 版なら +#define MAX_CPU 8 // 32-bit 版は少なくしておく +#define MAX_MEM_SIZE 0x7F000000 // 確保するメモリー領域の最大値 2032MB +#define MAX_MEM_SIZE32 0x50000000 // 32-bit OS で確保するメモリー領域の最大値 1280MB +#else +#define MAX_CPU 16 // 最大 CPU/Core 個数 (スレッド本数) +#endif + +#define MAX_LEN 1024 // ファイル名の最大文字数 (末尾のNULL文字も含む) +#define ADD_LEN 8 // 作業中にファイル名に追加する文字数 +#define EXT_LEN 16 // 拡張子として認識する最大文字数 +#define COMMENT_LEN 128 // コメントの最大文字数 +#define ALLOC_LEN 16384 // 可変長文字列を何文字ごとに確保するか +#define IO_SIZE 131072 // 16384 以上にすること +#define STACK_SIZE 131072 // 65536 以上にすること +#define MAX_SOURCE_NUM 32768 // ソース・ブロック数の最大値 +#define MAX_PARITY_NUM 65535 // パリティ・ブロック数の最大値 +#define MAX_BLOCK_SIZE 0x7FFFFFFC // 対応するブロック・サイズの最大値 2 GB +#define SEARCH_SIZE 1048576 // リカバリ・ファイルの検査単位 +#define UPDATE_TIME 1024 // 更新間隔 ms + + +// グローバル変数 +extern wchar_t recovery_file[MAX_LEN]; // リカバリ・ファイルのパス +extern wchar_t base_dir[MAX_LEN]; // ソース・ファイルの基準ディレクトリ +extern wchar_t ini_path[MAX_LEN]; // 検査結果ファイルのパス + +extern int base_len; // ソース・ファイルの基準ディレクトリの長さ +extern int recovery_limit; // 作成時はリカバリ・ファイルのサイズ制限 +extern int first_num; // 作成時は最初のパリティ・ブロック番号、検査時は初めて見つけた数 + +extern int file_num; // ソース・ファイルの数 +extern int entity_num; // 実体のあるファイルの数 (recovery set に含まれるファイル数) +extern int recovery_num; // リカバリ・ファイルの数 +extern int source_num; // ソース・ブロックの数 +extern int parity_num; // パリティ・ブロックの数 + +extern unsigned int block_size; // ブロック・サイズ +extern unsigned int split_size; // 分割サイズ +extern __int64 total_file_size; // 合計ファイル・サイズ + +extern int switch_v; // 検査レベル +extern int switch_b; // バックアップを作るか + +// 可変長サイズの領域にファイル名を記録する +extern wchar_t *list_buf; // ソース・ファイルのファイル名のリスト +extern int list_len; // ファイル・リストの文字数 +extern int list_max; // ファイル・リストの最大文字数 + +extern wchar_t *recv_buf; // リカバリ・ファイルのリスト +extern int recv_len; // ファイル・リストの文字数 +extern wchar_t *recv2_buf; // 有効なパケットを含むリカバリ・ファイルのリスト +extern int recv2_len; // ファイル・リストの文字数 + +extern wchar_t *list2_buf; // 指定された検査対象のファイル名のリスト +extern int list2_len; +extern int list2_max; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// 作成時 + +// ソース・ファイルの情報 +typedef struct { // 44バイト + unsigned char id[16]; // File ID + unsigned char hash[16]; // ファイルの先頭 16KB のハッシュ値 + __int64 size; // ファイル・サイズ (存在しない場合は -1) + int name; // ファイル名の開始位置 +} file_ctx_c; + +// ソース・ブロックの情報 +typedef struct { // 12バイト + int file; // 属するソース・ファイルの番号 + unsigned int size; // ソース・ファイルからの読み込みサイズ (ブロック・サイズ以下) + unsigned int crc; // ソース・ブロックの CRC-32 +} source_ctx_c; + +// パリティ・ブロックの情報 +typedef struct { // 12バイト + __int64 off; // リカバリ・ファイル内での書き込み開始位置 + int file; // 属するリカバリ・ファイルの番号 +} parity_ctx_c; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// 検査・修復時 + +// ソース・ファイルの情報 +typedef struct { // 72バイト + unsigned char id[16]; // File ID + unsigned char hash[32]; // ファイルのハッシュ値、と先頭 16KB のハッシュ値 + __int64 size; // ファイル・サイズ + int b_off; // ソース・ブロックの開始番号 + int b_num; // ソース・ブロックの個数 + int name; // ファイル名の開始位置 (存在しない場合は -1 or 0) + int name2; // 名前が異なる場合のファイル名の開始位置 + unsigned int state; // ファイルの状態 + // 128=そのファイルのチェックサムが存在しない + // ファイルの状態 0=完全, 1=消失, 2=破損, 6=破損で上書き, 16=追加, 32=消失して別名&移動, 40=破損して別名&移動 + // フォルダの状態 64=存在, 65=消失, 96=別名&移動 +} file_ctx_r; + +// ソース・ブロックの情報 +typedef struct { // 36バイト + int file; // 属するソース・ファイルの番号 + unsigned int size; // ソース・ファイルからの読み込みサイズ (ブロック・サイズ以下) + unsigned int crc; // ソース・ブロックの CRC-32 + unsigned char hash[20]; // ソース・ブロックの MD5 と CRC-32 + int exist; + // 0=存在しない, 1=完全なファイル内に存在する, 2=破損ファイル内に存在する、またはエラー訂正済み + // 3=内容は全て 0, 4=同じブロックが存在する, 5=CRCで内容を復元できる + // 検査中にそのファイル内で見つかった場合は +0x1000 する (検査成功後に消す) +} source_ctx_r; + +// パリティ・ブロックの情報 +typedef struct { // 16バイト + __int64 off; // リカバリ・ファイル内での読み込み開始位置 + int file; // 属するリカバリ・ファイルの番号 + int exist; // 0=存在しない, 1=存在する, 0x100=存在するけど利用せず +} parity_ctx_r; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +extern unsigned int cp_output; // Console Output Code Page + +// 指定された Code Page から UTF-16 に変換する +int cp_to_utf16(char *in, wchar_t *out, unsigned int cp); + +// Windown OS の UTF-16 から指定された Code Page に変換する +int utf16_to_cp(wchar_t *in, char *out, unsigned int cp); + +// Windows OS の UTF-8 から UTF-16 に変換する +void utf8_to_utf16(char *in, wchar_t *out); + +// Windown OS の UTF-16 から UTF-8 に変換する +void utf16_to_utf8(wchar_t *in, char *out); + +// 文字列が UTF-8 かどうかを判定する (0 = maybe UTF-8) +int check_utf8(unsigned char *text); + +// ファイル・パスから、先頭にある "\\?\" を省いて、指定された Code Page に変換する +int path_to_cp(wchar_t *path, char *out, unsigned int cp); + +// UTF-16 のファイル・パスを画面出力用の Code Page を使って表示する +void printf_cp(unsigned char *format, wchar_t *path); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ファイルの offset バイト目から size バイトのデータを buf に読み込む +int file_read_data( + HANDLE hFileRead, + __int64 offset, + unsigned char *buf, + unsigned int size); + +// ファイルの offset バイト目に size バイトのデータを buf から書き込む +int file_write_data( + HANDLE hFileWrite, + __int64 offset, + unsigned char *buf, + unsigned int size); + +// ファイルの offset バイト目に size バイトの指定値を書き込む +int file_fill_data( + HANDLE hFileWrite, + __int64 offset, + unsigned char value, + unsigned int size); + +// ファイルの指定バイト目から size バイトのデータを別のファイルに書き込む +int file_copy_data( + HANDLE hFileRead, + __int64 offset_read, + HANDLE hFileWrite, + __int64 offset_write, + unsigned int size); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// スライス断片用の構造体 +typedef struct { + int id; // ブロック番号 + int front_size; // 前半の占有サイズ + int rear_size; // 後半の占有サイズ +} flake_ctx; + +// スライス検査用の構造体 +typedef struct { + unsigned char *buf; // ブロック・サイズ *3 の作業領域 + // ブロック比較用 + int *order; // CRC-32 の順序を格納するバッファー + int block_count; // スライス検出で比較するブロックの数 + int index_shift; // インデックス・サーチ用のシフト量 + int short_count; // スライス検出で比較する半端なブロックの数 + unsigned int min_size; // 半端なブロックの最小サイズ + // 作業ファイル用 + int num; + HANDLE hFile_tmp; + int flake_count; // スライス断片を何個記録してるか + unsigned char *flk_buf; // スライス断片の記録領域 + // IO のマルチスレッド用 + volatile unsigned int size; + HANDLE volatile hFile; + HANDLE h; + HANDLE run; + HANDLE end; +} slice_ctx; + +// 修復中のテンポラリ・ファイルの名前を作る +void get_temp_name( + wchar_t *file_path, // ファイル・パス + wchar_t *temp_path); // テンポラリ・ファイルのパス + +// 作業用のゼロで埋められたテンポラリ・ファイルを作成する +int create_temp_file( + wchar_t *file_path, // ファイルのパス + __int64 file_size); // ファイルのサイズ + +// 作業用のソース・ファイルを開く +HANDLE handle_temp_file( + wchar_t *file_name, // ソース・ファイル名 + wchar_t *file_path); // 作業用、基準ディレクトリが入ってる + +// 上書き用のソース・ファイルを開く +HANDLE handle_write_file( + wchar_t *file_name, // ソース・ファイル名 + wchar_t *file_path, // 作業用、基準ディレクトリが入ってる + __int64 file_size); // 本来のファイルサイズ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ファイル・パスがファイル・リスト上に既に存在するか調べる +int search_file_path( + wchar_t *list, // ファイル・リスト + int total_len, // ファイル・リストの文字数 + wchar_t *search_file); // 検索するファイルのパス + +// ファイル・リストの内容を並び替える +void sort_list( + wchar_t *list, // ファイル・リスト + int total_len); // ファイル・リストの文字数 + +// ソース・ファイルのリストに新しいファイル名を追加する +int add_file_path(wchar_t *filename); // 追加するファイル名 + +// ファイル・リストから指定されたファイル・パスを取り除く +// 減らした後のファイル・リストの文字数を返す +int remove_file_path( + wchar_t *list, // ファイル・リスト + int total_len, // ファイル・リストの文字数 + int file_off); // 取り除くファイル・パスの位置 + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ファイル・パスからファイル名の位置を戻す +wchar_t * offset_file_name(wchar_t *file_path); + +// ファイル・パスからファイル名だけ取り出す +void get_file_name( + wchar_t *file_path, // ファイル・パス + wchar_t *file_name); // ファイル名 + +// ファイル・パスからディレクトリだけ取り出す、末尾は「\」か「/」 +void get_base_dir( + wchar_t *file_path, // ファイル・パス + wchar_t *base_path); // ディレクトリ + +// ディレクトリ記号の「\」を「/」に置換する +void unix_directory(wchar_t *path); + +// 絶対パスかどうかを判定する +int is_full_path(wchar_t *path); + +// ファイルのディレクトリ位置が同じかどうかを調べる +int compare_directory(wchar_t *path1, wchar_t *path2); + +// ワイルドカードを含む文字列をコピーする +int copy_wild(wchar_t *dst, wchar_t *src); + +// ワイルドカード('*', '?')を使ってユニコードのパスを比較する +int PathMatchWild( + wchar_t *text, // 比較する文字列 + wchar_t *wild); // ワイルドカード + +// ファイルのパスを除外リストと比較する +int exclude_path(wchar_t *path); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 相対パスを絶対パスに変換し、パスの先頭に "\\?\" を追加する +// 戻り値 : 0=エラー, 5~=新しいパスの長さ +int copy_path_prefix( + wchar_t *new_path, // 新しいパス + int max_len, // 新しいパスの最大長さ (末尾の null文字も含む) + wchar_t *src_path, // 元のパス (相対パスでもよい) + wchar_t *dir_path); // 相対パスの場合に基準となるディレクトリ (NULL ならカレント・ディレクトリ) + +// ファイル・パスから、先頭にある "\\?\" を省いた長さを戻す +int len_without_prefix(wchar_t *file_path); + +// ファイル・パスから、先頭にある "\\?\" を省いてコピーする +int copy_without_prefix( + wchar_t *dst_path, // コピー先 + wchar_t *src_path); // コピー元のパス + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 順番にサブ・ディレクトリを作成する +int make_dir( + wchar_t *file_path); // サブ・ディレクトリを含むファイル・パス + +// ファイルを置き換える (ファイル名の修正、テンポラリー・ファイルからの書き戻しなど) +int replace_file( + wchar_t *dest_path, // 置き換える先のパス (移動先、修正されたファイル名) + wchar_t *sorc_path); // 置き換える元のパス (移動元、現在のファイル名) + +// ファイルをどかす +void move_away_file( + wchar_t *file_path); // ファイル・パス + +// ファイルを指定サイズに縮小する +int shorten_file( + wchar_t *file_path, // ファイル・パス + __int64 new_size); + +// ファイル名が有効かどうか調べる +int check_filename( + wchar_t *name); // 検査するファイル名 + +// ファイル名が有効か確かめて、問題があれば浄化する +int sanitize_filename( + wchar_t *name, // 検査するファイル名 + file_ctx_r *files, // 各ソース・ファイルの情報 + int num); // 比較から除外するファイル番号 + +// リカバリ・ファイルのパスから拡張子とボリューム番号を取り除く +void get_base_filename( + wchar_t *file_path, // リカバリ・ファイルのパス + wchar_t *base_path, // 基準ファイル名のパス + wchar_t *file_ext); // 拡張子 (存在するなら) + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ハッシュ値を表示する +void print_hash(unsigned char hash[16]); + +// 64-bit 整数の平方根を求める +unsigned int sqrt64(__int64 num); + +// 32-bit 整数の平方根を求める +int sqrt32(int num); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +extern int cpu_num; +extern unsigned int cpu_flag, cpu_cache; +extern unsigned int memory_use; // メモリー使用量 0=auto, 1~7 -> 1/8 ~ 7/8 + +void check_cpu(void); +int check_OS64(void); + +// 空きメモリー量と制限値から使用できるメモリー量を計算する +size_t get_mem_size(size_t trial_alloc); + +// 記録装置の特性を調べる +int check_seek_penalty(wchar_t *dir_path); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +extern int prog_last; // 前回と同じ進捗状況は出力しないので記録しておく +extern int count_last; + +// 経過表示用 +int print_progress(int prog_now); +void print_progress_text(int prog_now, char *text); +int print_progress_file(int prog_now, int count_now, wchar_t *file_name); +void print_progress_done(void); + +// キャンセルと一時停止を行う +int cancel_progress(void); + +// エラー発生時にキャンセルできるようにする +int error_progress(int error_now, int error_last); + +// Win32 API のエラー・メッセージを表示する +void print_win32_err(void); + +// ファイルをゴミ箱に移す +int delete_file_recycle(wchar_t *file_path); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par2j/crc.c b/source/par2j/crc.c new file mode 100644 index 0000000..c9e76bd --- /dev/null +++ b/source/par2j/crc.c @@ -0,0 +1,419 @@ +// crc.c +// Copyright : 2022-02-09 Yutaka Sawada +// License : GPL + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 // Windows Vista or later +#endif + +#include + +#include +#include // MMX ~ SSE4.2 命令セットを使用する場合インクルード +#include // AES, CLMUL 命令セットを使用する場合インクルード + +#include "crc.h" + +extern unsigned int cpu_flag; // declared in common2.h + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// CRC-32 計算用 + +#define CRC32_POLY 0xEDB88320 // CRC-32-IEEE 802.3 (little endian) +unsigned int crc_table[256]; +unsigned int reverse_table[256]; // CRC-32 逆算用のテーブル + +// CRC 計算用のテーブルを作る +void init_crc_table(void) +{ + unsigned int i, j, r; + + for (i = 0; i < 256; i++){ // CRC-32 + r = i; + for (j = 0; j < 8; j++) + r = (r >> 1) ^ (CRC32_POLY & ~((r & 1) - 1)); + crc_table[i] = r; + } + + // crc の 最上位 1バイトからテーブルの位置がわかる + // まずは逆算用のテーブルを作る、テーブルを 8ビットずらして最下位に番号を入れておく + for (i = 0; i < 256; i++) + reverse_table[(crc_table[i] >> 24)] = (crc_table[i] << 8) | i; +} + +// CRC-32 を更新する +unsigned int crc_update_std(unsigned int crc, unsigned char *buf, unsigned int len) +{ + // 4バイト境界までは 1バイトずつ計算する + while ((len > 0) && (((ULONG_PTR)buf) & 3)){ + crc = crc_table[(crc & 0xFF) ^ (*buf++)] ^ (crc >> 8); + len--; + } + + // 4バイトごとに計算する + while (len >= 4){ + crc ^= *((unsigned int *)buf); + crc = crc_table[crc & 0xFF] ^ (crc >> 8); + crc = crc_table[crc & 0xFF] ^ (crc >> 8); + crc = crc_table[crc & 0xFF] ^ (crc >> 8); + crc = crc_table[crc & 0xFF] ^ (crc >> 8); + len -= 4; + buf += 4; + } + + // 余りは 1バイトずつ計算する + while (len--) + crc = crc_table[(crc & 0xFF) ^ (*buf++)] ^ (crc >> 8); + + return crc; +} + +// 内容が全て 0 のデータの CRC-32 を更新する +/* +unsigned int crc_update_zero(unsigned int crc, unsigned int len) +{ + while (len--) + crc = crc_table[crc & 0xFF] ^ (crc >> 8); + + return crc; +} +*/ + +// 内容が全て 0 のデータの CRC-32 を逆算するための関数 +unsigned int crc_reverse_zero(unsigned int crc, unsigned int len) +{ + // crc2 = 前の crc ^ 0xFFFFFFFF; + // crc = table[crc2 & 0xff] ^ (crc2 >> 8); + + //crc ^= 0xFFFFFFFF; // 最終処理を取り消す + while (len--) + crc = reverse_table[(crc >> 24)] ^ (crc << 8); + //crc ^= 0xFFFFFFFF; // 最終処理をし直す + + return crc; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// CRC-32 with PCLMULQDQ Instruction is based on below source code. + +/* + * Compute the CRC32 using a parallelized folding approach with the PCLMULQDQ + * instruction. + * + * A white paper describing this algorithm can be found at: + * http://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * Authors: + * Wajdi Feghali + * Jim Guilford + * Vinodh Gopal + * Erdinc Ozturk + * Jim Kukunas + * + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +// PCLMULQDQ を使って CRC-32 を更新する +unsigned int crc_update(unsigned int crc, unsigned char *buf, unsigned int len) +{ + __declspec( align(16) ) unsigned int buf128[4]; + unsigned int i; + __m128i crc128, data128, temp128, two_k128; + + // special case; shorter than 19 bytes or miss-alignment + if (((cpu_flag & 8) == 0) || (len < 19)) + return crc_update_std(crc, buf, len); + // 4バイト境界までは 1バイトずつ計算する + while (((ULONG_PTR)buf) & 3){ + crc = crc_table[(crc & 0xFF) ^ (*buf++)] ^ (crc >> 8); + len--; + } + + i = ((ULONG_PTR)buf) & 12; + if (i != 0){ // read first 4, 8, or 12 bytes until memory alignment + i = 16 - i; // how many bytes to read + len -= i; + i /= 4; + buf128[0] = 0; + buf128[1] = 0; + buf128[2] = 0; + buf128[3] = 0; + buf128[4 - i] ^= crc; // set initial value + while (i > 0){ + buf128[4 - i] ^= *((unsigned int *)buf); + buf += 4; + i--; + } + } else { // read first 16 bytes + buf128[0] = ((unsigned int *)buf)[0]; + buf128[1] = ((unsigned int *)buf)[1]; + buf128[2] = ((unsigned int *)buf)[2]; + buf128[3] = ((unsigned int *)buf)[3]; + buf128[0] ^= crc; // set initial value + len -= 16; + buf += 16; + } + crc128 = _mm_load_si128((__m128i *)buf128); + + // set two constants; K1 = 0xccaa009e, K2 = 0x1751997d0 + two_k128 = _mm_set_epi32(0x00000001, 0x751997d0, 0x00000000, 0xccaa009e); + + // per 16 bytes + while (len >= 16){ + data128 = _mm_load_si128((__m128i *)buf); + + temp128 = _mm_clmulepi64_si128(crc128, two_k128, 0x10); + crc128 = _mm_clmulepi64_si128(crc128, two_k128, 0x01); + data128 = _mm_xor_si128(data128, temp128); + crc128 = _mm_xor_si128(crc128, data128); + + len -= 16; + buf += 16; + } + + // set two constants; K5 = 0xccaa009e, K6 = 0x163cd6124 + two_k128 = _mm_set_epi32(0x00000001, 0x63cd6124, 0x00000000, 0xccaa009e); + + // reduce from 128-bit to 96-bit by multiplication with K5 + data128 = _mm_clmulepi64_si128(crc128, two_k128, 0); + temp128 = _mm_srli_si128(crc128, 8); + data128 = _mm_xor_si128(data128, temp128); + // reduce from 96-bit to 64-bit by multiplication with K6 + temp128 = _mm_slli_si128(data128, 4); + crc128 = _mm_clmulepi64_si128(temp128, two_k128, 0x10); + crc128 = _mm_xor_si128(crc128, data128); + + // set two constants; K7 = 0x1f7011640, K8 = 0x1db710640 + two_k128 = _mm_set_epi32(0x00000001, 0xdb710640, 0x00000001, 0xf7011640); + + // Barrett Reduction from 64-bit to 32-bit + data128 = _mm_and_si128(crc128, _mm_set_epi32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000)); + temp128 = _mm_clmulepi64_si128(data128, two_k128, 0); + temp128 = _mm_xor_si128(temp128, data128); + temp128 = _mm_and_si128(temp128, _mm_set_epi32(0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF)); + crc128 = _mm_clmulepi64_si128(temp128, two_k128, 0x10); + crc128 = _mm_xor_si128(crc128, temp128); + crc128 = _mm_xor_si128(crc128, data128); + + crc = _mm_extract_epi32(crc128, 2); + + // per 1 byte rest + while (len--) + crc = crc_table[(crc & 0xFF) ^ (*buf++)] ^ (crc >> 8); + + return crc; +} + +// 内容が全て 0 のデータの CRC-32 を更新する +unsigned int crc_update_zero(unsigned int crc, unsigned int len) +{ + __m128i crc128, data128, temp128, two_k128; + + // special case; shorter than 16 bytes + if (((cpu_flag & 8) == 0) || (len < 16)){ + while (len--) + crc = crc_table[crc & 0xFF] ^ (crc >> 8); + + return crc; + } + + // first 16 bytes + len -= 16; + crc128 = _mm_cvtsi32_si128(crc); // set initial value + + // set two constants; K1 = 0xccaa009e, K2 = 0x1751997d0 + two_k128 = _mm_set_epi32(0x00000001, 0x751997d0, 0x00000000, 0xccaa009e); + + // per 16 bytes + while (len >= 16){ + temp128 = _mm_clmulepi64_si128(crc128, two_k128, 0x10); + crc128 = _mm_clmulepi64_si128(crc128, two_k128, 0x01); + crc128 = _mm_xor_si128(crc128, temp128); + + len -= 16; + } + + // set two constants; K5 = 0xccaa009e, K6 = 0x163cd6124 + two_k128 = _mm_set_epi32(0x00000001, 0x63cd6124, 0x00000000, 0xccaa009e); + + // reduce from 128-bit to 96-bit by multiplication with K5 + data128 = _mm_clmulepi64_si128(crc128, two_k128, 0); + temp128 = _mm_srli_si128(crc128, 8); + data128 = _mm_xor_si128(data128, temp128); + // reduce from 96-bit to 64-bit by multiplication with K6 + temp128 = _mm_slli_si128(data128, 4); + crc128 = _mm_clmulepi64_si128(temp128, two_k128, 0x10); + crc128 = _mm_xor_si128(crc128, data128); + + // set two constants; K7 = 0x1f7011640, K8 = 0x1db710640 + two_k128 = _mm_set_epi32(0x00000001, 0xdb710640, 0x00000001, 0xf7011640); + + // Barrett Reduction from 64-bit to 32-bit + data128 = _mm_and_si128(crc128, _mm_set_epi32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000)); + temp128 = _mm_clmulepi64_si128(data128, two_k128, 0); + temp128 = _mm_xor_si128(temp128, data128); + temp128 = _mm_and_si128(temp128, _mm_set_epi32(0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF)); + crc128 = _mm_clmulepi64_si128(temp128, two_k128, 0x10); + crc128 = _mm_xor_si128(crc128, temp128); + crc128 = _mm_xor_si128(crc128, data128); + + crc = _mm_extract_epi32(crc128, 2); + + // per 1 byte rest + while (len--) + crc = crc_table[crc & 0xFF] ^ (crc >> 8); + + return crc; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// par2cmdline を参考にした関数 +// window サイズの CRC を計算してある所に、1バイトずつ追加と削除をして、CRC を更新する + +// This file is part of par2cmdline (a PAR 2.0 compatible file verification and +// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. +// +// Copyright (c) 2003 Peter Brian Clements +// +// par2cmdline is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. + +unsigned int window_table[256]; // 詳細検査で CRC-32 をスライドさせる為のテーブル +unsigned int window_mask = 0; +// 先に window_mask を計算してソース・ブロックのチェックサムに XOR することで、 +// 初期値と最終処理の 0xFFFFFFFF の影響を消す。 +// そうすることで、スライド時に window_mask を使わないで済む。 + +// Slide the CRC along a buffer by one character (removing the old and adding the new). +// The new character is added using the main CCITT CRC32 table, and the old character +// is removed using the windowtable. +/* +unsigned int crc_slide_char(unsigned int crc, unsigned char chNew, unsigned char chOld){ + return crc_table[(crc & 0xFF) ^ chNew] ^ (crc >> 8) ^ window_table[chOld]; +} +*/ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// Jonathan Camacho が改良した計算方法 + +// CRC( XOR(A,B) ) == XOR(CRC(A), CRC(B)) +// bit twiddling to construct windowtable and windowmask +// window_table と window_mask の計算が 257倍? 速くなるらしい。 + +static void compute_result_table(unsigned int result, unsigned int result_array[32]) +{ + int i; + + for (i = 0; i < 32; i++){ + result = ((result >> 1) & 0x7FFFFFFFL) ^ ((result & 1) ? CRC32_POLY : 0); + result_array[i] = result; + } +} + +static void fast_compute_crc_table(unsigned int result_array[8], unsigned int table[256]) +{ + int i, j, value; + unsigned int new_crc; + + table[0] = 0; // g_CrcTable[0 & 0xff] ^ (0 >> 8) は常に 0 + for (i = 1; i < 256; i++){ + //Firstly, find the correct masks that we need. + //result_array[0] is 128, + //result_array[1] is 64 + //result_array[2] is 32 + //result_array[3] is 16 + //result_array[4] is 8 + //result_array[5] is 4 + //result_array[6] is 2 + //result_array[7] is 1. + //The other values in result_array are not needed right now. + //So basically, for all values of i 0..255 we need to xor + //together the result_array values that it represents. + new_crc = 0; + value = i; + for (j = 0; j < 8; j++){ + new_crc = new_crc ^ ( (value & 128) ? result_array[j] : 0) ; + value = value << 1; + } + table[i] = new_crc; +// printf("table[%d] = %08x\n", i, table[i]); + } +} + +/* +unsigned int onepass_window_gen(unsigned int window_size, unsigned int window_table[256]) +{ + unsigned int result = 1; + unsigned int i; + unsigned int masked_result_array[32]; + unsigned int window_mask; + + if (window_size > 4){ + result = crc_update_zero(result, window_size - 4); + compute_result_table(result, masked_result_array); + window_mask = 0; + for (i = 0; i < 32; i++){ + window_mask = window_mask ^ masked_result_array[i]; + } + window_mask = window_mask ^ ~0; + for (i = 0; i < 4; i++){ + result = crc_table[(result & 0xFF)] ^ (result >> 8); + } + compute_result_table(result, masked_result_array); + fast_compute_crc_table(masked_result_array, window_table); + + } else { // 普通? に計算する + window_table[0] = 0; // crc_table[0 & 0xff] ^ (0 >> 8) は常に 0 + for (i = 1; i < 256; i++){ + window_table[i] = crc_update_zero(crc_table[i], window_size); // 0が window サイズ個並んだデータの CRC + } + window_mask = crc_update_zero(0xFFFFFFFF, window_size); + window_mask ^= 0xFFFFFFFF; + } + +// printf("window_mask = %08x\n", window_mask); +// for (i = 0; i < 256; i++) +// printf("window_table[%d] = %08x\n", i, window_table[i]); + return window_mask; +} +*/ +void onepass_window_gen(unsigned int window_size) +{ + unsigned int result = 1; + unsigned int i; + unsigned int masked_result_array[32]; + + if (window_size <= 4){ + window_mask = 0x2144DF1C; // 4バイトの 0 に対する CRC-32 + return; // ブロック・サイズが 4以下の時はスライド検査しない + } + + result = crc_update_zero(result, window_size - 4); + compute_result_table(result, masked_result_array); + window_mask = 0; + for (i = 0; i < 32; i++){ + window_mask = window_mask ^ masked_result_array[i]; + } + window_mask = window_mask ^ 0xFFFFFFFF; + for (i = 0; i < 4; i++){ + result = crc_table[(result & 0xFF)] ^ (result >> 8); + } + compute_result_table(result, masked_result_array); + fast_compute_crc_table(masked_result_array, window_table); +} + +// 初期値と最終処理の 0xFFFFFFFF を使ってない CRC のスライドには window_mask は必要ない +void onepass_window_gen_short(unsigned int short_size, unsigned int short_table[256]) +{ + unsigned int result = 1; + unsigned int masked_result_array[32]; + + // short_size must be larger than 4-byte + result = crc_update_zero(result, short_size); + compute_result_table(result, masked_result_array); + fast_compute_crc_table(masked_result_array, short_table); +} + diff --git a/source/par2j/crc.h b/source/par2j/crc.h new file mode 100644 index 0000000..428e80d --- /dev/null +++ b/source/par2j/crc.h @@ -0,0 +1,53 @@ +#ifndef _CRC_H_ +#define _CRC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// CRC-32 計算用 + +extern unsigned int crc_table[256]; +extern unsigned int reverse_table[256]; // CRC-32 逆算用のテーブル + +// CRC 計算用のテーブルを作る +void init_crc_table(void); + +// CRC-32 を更新する +unsigned int crc_update(unsigned int crc, unsigned char *buf, unsigned int len); +unsigned int crc_update_std(unsigned int crc, unsigned char *buf, unsigned int len); + +// 全て 0 のデータの CRC-32 を更新する +unsigned int crc_update_zero(unsigned int crc, unsigned int len); + +// 内容が全て 0 のデータの CRC-32 を逆算するための関数 +unsigned int crc_reverse_zero(unsigned int crc, unsigned int len); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// par2cmdline を参考にした関数 +// window サイズの CRC を計算してある所に、1バイトずつ追加と削除をして、CRC を更新する +extern unsigned int window_table[256]; +extern unsigned int window_mask; + +void onepass_window_gen(unsigned int window_size); +void onepass_window_gen_short(unsigned int short_size, unsigned int short_table[256]); + +// マクロなら +#define CRC_SLIDE_CHAR(x,y,z) (crc_table[((x) & 0xFF) ^ (y)] ^ ((x) >> 8) ^ window_table[z]) + +/* +// インライン展開なら +__inline unsigned int crc_slide_char(unsigned int crc, unsigned char chNew, unsigned char chOld){ + return crc_table[(crc & 0xFF) ^ chNew] ^ (crc >> 8) ^ window_table[chOld]; +} +*/ + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/source/par2j/create.c b/source/par2j/create.c new file mode 100644 index 0000000..5fb587c --- /dev/null +++ b/source/par2j/create.c @@ -0,0 +1,2240 @@ +// create.c +// Copyright : 2022-02-16 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 "common2.h" +#include "phmd5.h" +#include "md5_crc.h" +#include "version.h" +#include "gf16.h" +#include "create.h" + +//#define TIMER // 実験用 + +// ソート時に項目を比較する +static int sort_cmp(const void *elem1, const void *elem2) +{ + const file_ctx_c *file1, *file2; + int i; + + file1 = elem1; + file2 = elem2; + + // サイズが 0 の空ファイルやフォルダは non-recovery set に含める + if (file1->size == 0){ + if (file2->size > 0) + return 1; + } else { + if (file2->size == 0) + return -1; + } + + // File ID の順に並び替える + for (i = 15; i >= 0; i--){ + if (file1->id[i] > file2->id[i]){ + return 1; // 1 > 2 + } else if (file1->id[i] < file2->id[i]){ + return -1; // 1 < 2 + } + } + return 0; // 1 = 2 +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// PAR 2.0 のパケット・ヘッダーを作成する +void set_packet_header( + unsigned char *buf, // ヘッダーを格納する 64バイトのバッファー + unsigned char *set_id, // Recovery Set ID、16バイト + int type_num, // そのパケットの型番号 + unsigned int body_size) // パケットのデータ・サイズ +{ + // Magic sequence + memcpy(buf, "PAR2\0PKT", 8); + // Length of the entire packet + body_size += 64; // パケット全体のサイズにする + memcpy(buf + 8, &body_size, 4); + memset(buf + 12, 0, 4); + // MD5 Hash of packet は後で計算する + // Recovery Set ID + memcpy(buf + 32, set_id, 16); + // Type + memcpy(buf + 48, "PAR 2.0\0", 8); + memset(buf + 56, 0, 8); + switch (type_num){ + case 1: // Main packet + memcpy(buf + 56, "Main", 4); + break; + case 2: // File Description packet + memcpy(buf + 56, "FileDesc", 8); + break; + case 3: // Input File Slice Checksum packet + memcpy(buf + 56, "IFSC", 4); + break; + case 4: // Recovery Slice packet + memcpy(buf + 56, "RecvSlic", 8); + break; + case 5: // Creator packet + memcpy(buf + 56, "Creator", 7); + break; + case 10: // Unicode Filename packet + memcpy(buf + 56, "UniFileN", 8); + break; + case 11: // ASCII Comment packet + memcpy(buf + 56, "CommASCI", 8); + break; + case 12: // Unicode Comment packet + memcpy(buf + 56, "CommUni", 7); + break; + } +} + +// ソース・ファイルの情報を集める +int get_source_files(file_ctx_c *files) +{ + unsigned char work_buf[16 + 8 + (MAX_LEN * 3)]; + unsigned char hash0[16] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, + 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e}; + wchar_t file_name[MAX_LEN], file_path[MAX_LEN]; + int i, list_off = 0, num, len, max = 0; + WIN32_FILE_ATTRIBUTE_DATA AttrData; + + wcscpy(file_path, base_dir); + for (num = 0; num < file_num; num++){ + files[num].name = list_off; // ファイル名の位置を記録する + wcscpy(file_name, list_buf + list_off); + // ファイル名が駄目だと警告する + if (check_filename(file_name) != 0){ + if (max == 0){ + max = 1; + printf("\nWarning about filenames :\n"); + } + utf16_to_cp(file_name, work_buf, cp_output); + printf("file%d: \"%s\" is invalid\n", num, work_buf); + } + //list_off += (wcslen(file_name) + 1); + while (list_buf[list_off] != 0) + list_off++; + list_off++; + + // 属性を調べる + wcscpy(file_path + base_len, file_name); + if (!GetFileAttributesEx(file_path, GetFileExInfoStandard, &AttrData)){ + print_win32_err(); + printf_cp("GetFileAttributesEx, %s\n", file_name); + return 1; + } + if (AttrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ // フォルダなら + memcpy(files[num].hash, hash0, 16); + files[num].size = 0; + } else { // ファイルなら + files[num].size = ((__int64)AttrData.nFileSizeHigh << 32) | (unsigned __int64)AttrData.nFileSizeLow; + if (files[num].size == 0){ // 空のファイルなら + memcpy(files[num].hash, hash0, 16); + } else { + // ファイルの先頭 16KB 分のハッシュ値を計算する + if (file_md5_16(file_path, files[num].hash)){ + printf_cp("file_md5_16, %s\n", file_name); + return 1; + } + } + } + + // File ID を計算する + memcpy(work_buf, files[num].hash, 16); + memcpy(work_buf + 16, &(files[num].size), 8); + unix_directory(file_name); // 記録時のディレクトリ記号は「/」にする + utf16_to_utf8(file_name, work_buf + 24); + len = (int)strlen(work_buf + 24); // File ID 計算時のファイル名のサイズは 4の倍数にしない + data_md5(work_buf, 24 + len, files[num].id); + // 他のファイルと同じ File ID になってないか調べる + do { + for (i = 0; i < num; i++){ + if (memcmp(files[num].id, files[i].id, 16) == 0){ // File ID が同じなら + files[num].id[0] += 1; // 最下位バイトを変化させる + break; + } + } + } while (i < num); // File ID を必ずユニークな値にする + } + + // ファイルを File ID の順に並び替える + qsort(files, file_num, sizeof(file_ctx_c), sort_cmp); + + return 0; +} + +// 共通パケットを作成する +int set_common_packet( + unsigned char *buf, + int *packet_num, // 共通パケットの数 + int switch_u, // ユニコードのファイル名も記録する + file_ctx_c *files) +{ + char ascii_buf[MAX_LEN * 3]; + unsigned char set_id[16], file_hash[16], *main_packet_buf; + unsigned char hash0[16] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, + 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e}; + wchar_t file_name[MAX_LEN]; + int i, err, off, off2, num, len, data_size, main_packet_size; + unsigned int time_last; + __int64 prog_now = 0; + +#ifdef TIMER +unsigned int time_start = GetTickCount(); +#endif + print_progress_text(0, "Computing file hash"); + + // 最初に Main packet を作成しておく + main_packet_size = 12 + (file_num * 16); + main_packet_buf = (unsigned char *)malloc(main_packet_size); + if (main_packet_buf == NULL){ + printf("malloc, %d\n", main_packet_size); + return 1; + } + memcpy(main_packet_buf, &block_size, 4); + memset(main_packet_buf + 4, 0, 4); + memcpy(main_packet_buf + 8, &entity_num, 4); + for (num = 0; num < file_num; num++) // 並び替えられた順序で File ID をコピーする + memcpy(main_packet_buf + (12 + (16 * num)), files[num].id, 16); + data_md5(main_packet_buf, main_packet_size, set_id); // Recovery Set ID を計算する + *packet_num = 1; + + // ファイルごとのパケットを作成する + time_last = GetTickCount(); + off = 0; + for (num = 0; num < file_num; num++){ + // File Description packet + off2 = off; // 位置を記録しておく + memcpy(buf + (off + 64), files[num].id, 16); + memcpy(buf + (off + 64 + 32), files[num].hash, 16); // 全体のハッシュ値は後で書き込む + memcpy(buf + (off + 64 + 48), &(files[num].size), 8); + // ファイル名を UTF-8 に変換する + wcscpy(file_name, list_buf + files[num].name); + unix_directory(file_name); // 記録時のディレクトリ記号は「/」にする + utf16_to_utf8(file_name, ascii_buf); + len = (int)strlen(ascii_buf); + for (i = len + 1; (i < len + 4) || (i < MAX_LEN * 3); i++) + ascii_buf[i] = 0; + len = (len + 3) & 0xFFFFFFFC; + memcpy(buf + (off + 64 + 56), ascii_buf, len); + data_size = 56 + len; + set_packet_header(buf + off, set_id, 2, data_size); // パケット・ヘッダーを作成する + //data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する + off += (64 + data_size); + (*packet_num)++; + + if (files[num].size > 0){ // Input File Slice Checksum packet + // QuickPar はなぜかサイズが 0 のファイルに対してもチェックサムのパケットが必要・・・ + // 無意味なパケットを避けるために non-recovery set を使った方がいい? + memcpy(buf + (off + 64), files[num].id, 16); + // ファイルの MD5 ハッシュ値とブロックのチェックサムを同時に計算する + err = file_hash_crc(list_buf + files[num].name, files[num].size, file_hash, buf + (off + 64 + 16), &time_last, &prog_now); + if (err){ + if (err == 1) + printf_cp("file_hash_crc, %s\n", file_name); + off = err; + goto error_end; + } + len = (int)((files[num].size + (__int64)block_size - 1) / block_size); + // ファイルの MD5-16k はもう不要なので、ブロックの CRC-32 に変更する + memset(files[num].hash, 0, 16); + for (i = 0; i < len; i++){ // XOR して 16バイトに減らす + memcpy(&err, buf + (off + 64 + 16 + 20 * i + 16), 4); + ((unsigned int *)files[num].hash)[i & 3] ^= err; + } +// printf("%d: 0x%08X %08X %08X %08X\n", num, +// ((unsigned int *)files[num].hash)[0], ((unsigned int *)files[num].hash)[1], +// ((unsigned int *)files[num].hash)[2], ((unsigned int *)files[num].hash)[3]); + i = 16 + 20 * len; // パケット内容のサイズ + set_packet_header(buf + off, set_id, 3, i); // パケット・ヘッダーを作成する + data_md5(buf + (off + 32), 32 + i, buf + (off + 16)); // パケットの MD5 を計算する + off += (64 + i); + (*packet_num)++; + } else { + memcpy(file_hash, hash0, 16); + } + + // File Description packet の続き + memcpy(buf + (off2 + 64 + 16), file_hash, 16); // 計算したハッシュ値をここで書き込む + data_md5(buf + (off2 + 32), 32 + data_size, buf + (off2 + 16)); // パケットの MD5 を計算する + + if (switch_u != 0){ // ユニコードのファイル名 + // 指定があってもファイル名が ASCII 文字だけならユニコードのパケットは作らない + len = (int)wcslen(file_name); + for (i = 0; i < len; i++){ + if (file_name[i] != ascii_buf[i]) + break; + } + if (i < len){ + // Unicode Filename packet + memcpy(buf + (off + 64), files[num].id, 16); + len = (int)wcslen(file_name) * 2; + memcpy(buf + (off + 64 + 16), file_name, len); + if (len & 3) + memset(buf + (off + 64 + 16 + len), 0, 2); + data_size = 16 + ((len + 3) & 0xFFFFFFFC); + set_packet_header(buf + off, set_id, 10, data_size); // パケット・ヘッダーを作成する + data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する + off += (64 + data_size); + (*packet_num)++; + } + } + } + print_progress_done(); // 改行して行の先頭に戻しておく + + // Main packet + set_packet_header(buf + off, set_id, 1, main_packet_size); // パケット・ヘッダーを作成する + memcpy(buf + (off + 64), main_packet_buf, main_packet_size); + data_md5(buf + (off + 32), 32 + main_packet_size, buf + (off + 16)); // パケットの MD5 を計算する + off += (64 + main_packet_size); + +#ifdef TIMER +time_start = GetTickCount() - time_start; +printf("hash %d.%03d sec", time_start / 1000, time_start % 1000); +if (time_start > 0){ + time_start = (int)((total_file_size * 125) / ((__int64)time_start * 131072)); + printf(", %d MB/s\n", time_start); +} else { + printf("\n"); +} +#endif + +error_end: + free(main_packet_buf); + return off; +} + +#define MAX_MULTI_READ 4 // SSDで同時に読み込む最大ファイル数 + +// SSD 上で複数ファイルのハッシュ値を同時に求めるバージョン +int set_common_packet_multi( + unsigned char *buf, + int *packet_num, // 共通パケットの数 + int switch_u, // ユニコードのファイル名も記録する + file_ctx_c *files) +{ + char ascii_buf[MAX_LEN * 3]; + unsigned char set_id[16], file_hash[16], *main_packet_buf; + unsigned char hash0[16] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, + 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e}; + wchar_t file_name[MAX_LEN]; + int i, err = 0, off, off2, num, len, data_size, main_packet_size, multi_read; + unsigned int rv, time_last; + __int64 prog_now = 0, prog_loop; + HANDLE hSub[MAX_MULTI_READ]; + FILE_HASH_TH th[MAX_MULTI_READ]; + +#ifdef TIMER +unsigned int time_start = GetTickCount(); +#endif + + memset(hSub, 0, sizeof(HANDLE) * MAX_MULTI_READ); + memset(th, 0, sizeof(FILE_HASH_TH) * MAX_MULTI_READ); + // Core数に応じてスレッド数を増やす + if ((memory_use & 32) != 0){ // NVMe SSD + if (cpu_num >= 8){ // 8 ~ 16 Cores + multi_read = 4; + } else { // 3 Cores + Hyper-threading, or 4 ~ 7 Cores + multi_read = 3; + } + } else { // SATA SSD + multi_read = 2; + } + if (multi_read > entity_num) + multi_read = entity_num; +#ifdef TIMER + printf("cpu_num = %d, entity_num = %d, multi_read = %d\n", cpu_num, entity_num, multi_read); +#endif + + print_progress_text(0, "Computing file hash"); + + // 最初に Main packet を作成しておく + main_packet_size = 12 + (file_num * 16); + main_packet_buf = (unsigned char *)malloc(main_packet_size); + if (main_packet_buf == NULL){ + printf("malloc, %d\n", main_packet_size); + return 1; + } + memcpy(main_packet_buf, &block_size, 4); + memset(main_packet_buf + 4, 0, 4); + memcpy(main_packet_buf + 8, &entity_num, 4); + for (num = 0; num < file_num; num++) // 並び替えられた順序で File ID をコピーする + memcpy(main_packet_buf + (12 + (16 * num)), files[num].id, 16); + data_md5(main_packet_buf, main_packet_size, set_id); // Recovery Set ID を計算する + *packet_num = 1; + + // ファイルごとのパケットを作成する + time_last = GetTickCount(); + off = 0; + for (num = 0; num < file_num; num++){ + // File Description packet + off2 = off; // 位置を記録しておく + memcpy(buf + (off + 64), files[num].id, 16); + memcpy(buf + (off + 64 + 32), files[num].hash, 16); // 全体のハッシュ値は後で書き込む + memcpy(buf + (off + 64 + 48), &(files[num].size), 8); + // ファイル名を UTF-8 に変換する + wcscpy(file_name, list_buf + files[num].name); + unix_directory(file_name); // 記録時のディレクトリ記号は「/」にする + utf16_to_utf8(file_name, ascii_buf); + len = (int)strlen(ascii_buf); + for (i = len + 1; (i < len + 4) || (i < MAX_LEN * 3); i++) + ascii_buf[i] = 0; + len = (len + 3) & 0xFFFFFFFC; + memcpy(buf + (off + 64 + 56), ascii_buf, len); + data_size = 56 + len; // data_size = off - off2 - 64 なのでサイズを記録しなくても逆算できる + set_packet_header(buf + off, set_id, 2, data_size); // パケット・ヘッダーを作成する + //data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する + off += (64 + data_size); + (*packet_num)++; + + if (files[num].size > 0){ // Input File Slice Checksum packet + // QuickPar はなぜかサイズが 0 のファイルに対してもチェックサムのパケットが必要・・・ + // 無意味なパケットを避けるために non-recovery set を使った方がいい? + memcpy(buf + (off + 64), files[num].id, 16); + len = (int)((files[num].size + (__int64)block_size - 1) / block_size); // ブロック数 + data_size = 16 + 20 * len; // パケット内容のサイズ + set_packet_header(buf + off, set_id, 3, data_size); // パケット・ヘッダーを作成する + if (multi_read < 2){ // ファイルを一個ずつ処理する + // ファイルの MD5 ハッシュ値とブロックのチェックサムを同時に計算する + err = file_hash_crc(file_name, files[num].size, file_hash, buf + (off + 64 + 16), &time_last, &prog_now); + if (err){ + if (err == 1) + printf_cp("file_hash_crc, %s\n", file_name); + goto error_end; + } + // ファイルの MD5-16k はもう不要なので、ブロックの CRC-32 に変更する + memset(files[num].hash, 0, 16); + for (i = 0; i < len; i++){ // XOR して 16バイトに減らす + memcpy(&rv, buf + (off + 64 + 16 + 20 * i + 16), 4); + ((unsigned int *)files[num].hash)[i & 3] ^= rv; + } +// printf("%d: 0x%08X %08X %08X %08X\n", num, +// ((unsigned int *)files[num].hash)[0], ((unsigned int *)files[num].hash)[1], +// ((unsigned int *)files[num].hash)[2], ((unsigned int *)files[num].hash)[3]); + data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する + + // File Description packet の続き + memcpy(buf + (off2 + 64 + 16), file_hash, 16); // 計算したハッシュ値をここで書き込む + data_md5(buf + (off2 + 32), 32 + (off - off2 - 64), buf + (off2 + 16)); // パケットの MD5 を計算する + + } else { // 複数ファイルを同時に処理する + if (num < multi_read){ // 最初の何個かは順番にスレッドを割り当てる + i = num; + } else { // 全て使用済みなら、どれかが終わるのを待つ + do { + rv = WaitForMultipleObjects(multi_read, hSub, FALSE, UPDATE_TIME / 2); + if (GetTickCount() - time_last >= UPDATE_TIME){ + prog_loop = 0; + for (i = 0; i < multi_read; i++) + prog_loop += th[i].loop; // サブ・スレッドの進捗状況を合計する + prog_loop *= IO_SIZE; + prog_loop += prog_now; + if (print_progress((int)((prog_loop * 1000) / total_file_size))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + } while (rv == WAIT_TIMEOUT); + if (rv == WAIT_FAILED){ + print_win32_err(); + err = 1; + goto error_end; + } + i = rv - WAIT_OBJECT_0; // 終了したスレッド + if (GetExitCodeThread(hSub[i], &rv) == 0){ // サブ・スレッドのエラー確認 + print_win32_err(); + err = 1; + goto error_end; + } else if (rv != 0){ + err = rv; + goto error_end; + } + CloseHandle(hSub[i]); + prog_now += th[i].file_size; + } + //printf("thread = %d, size = %I64d\n", i, files[num].size); + th[i].file_name = list_buf + files[num].name; + th[i].file_size = files[num].size; + th[i].hash = buf + (off2 + 64 + 16); + th[i].sum = buf + (off + 64 + 16); + th[i].crc = (unsigned int *)(files[num].hash); + th[i].loop = 0; + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する + hSub[i] = (HANDLE)_beginthreadex(NULL, STACK_SIZE + MAX_LEN * 2, file_hash_crc2, (LPVOID)&(th[i]), 0, NULL); + if (hSub[i] == NULL){ + print_win32_err(); + printf("error, sub-thread\n"); + err = 1; + goto error_end; + } + } + off += (64 + data_size); + (*packet_num)++; + + } else { + // File Description packet の続き + memcpy(buf + (off2 + 64 + 16), hash0, 16); // 予め計算してあるハッシュ値をコピーする + data_md5(buf + (off2 + 32), 32 + (off - off2 - 64), buf + (off2 + 16)); // パケットの MD5 を計算する + } + + if (switch_u != 0){ // ユニコードのファイル名 + // 指定があってもファイル名が ASCII 文字だけならユニコードのパケットは作らない + len = (int)wcslen(file_name); + for (i = 0; i < len; i++){ + if (file_name[i] != ascii_buf[i]) + break; + } + if (i < len){ + // Unicode Filename packet + memcpy(buf + (off + 64), files[num].id, 16); + len = (int)wcslen(file_name) * 2; + memcpy(buf + (off + 64 + 16), file_name, len); + if (len & 3) + memset(buf + (off + 64 + 16 + len), 0, 2); + data_size = 16 + ((len + 3) & 0xFFFFFFFC); + set_packet_header(buf + off, set_id, 10, data_size); // パケット・ヘッダーを作成する + data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する + off += (64 + data_size); + (*packet_num)++; + } + } + } + + // Main packet + set_packet_header(buf + off, set_id, 1, main_packet_size); // パケット・ヘッダーを作成する + memcpy(buf + (off + 64), main_packet_buf, main_packet_size); + data_md5(buf + (off + 32), 32 + main_packet_size, buf + (off + 16)); // パケットの MD5 を計算する + off += (64 + main_packet_size); + + if (multi_read >= 2){ // 全てのサブ・スレッドが終了するまで待つ + do { + rv = WaitForMultipleObjects(multi_read, hSub, TRUE, UPDATE_TIME / 2); + if (GetTickCount() - time_last >= UPDATE_TIME){ + prog_loop = 0; + for (i = 0; i < multi_read; i++) + prog_loop += th[i].loop; // サブ・スレッドの進捗状況を合計する + prog_loop *= IO_SIZE; + prog_loop += prog_now; + if (print_progress((int)((prog_loop * 1000) / total_file_size))){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + } while (rv == WAIT_TIMEOUT); + if (rv == WAIT_FAILED){ + print_win32_err(); + err = 1; + goto error_end; + } + } + print_progress_done(); // 改行して行の先頭に戻しておく +#ifdef TIMER +time_start = GetTickCount() - time_start; +printf("hash %d.%03d sec", time_start / 1000, time_start % 1000); +if (time_start > 0){ + time_start = (int)((total_file_size * 125) / ((__int64)time_start * 131072)); + printf(", %d MB/s\n", time_start); +} else { + printf("\n"); +} +#endif + +error_end: + free(main_packet_buf); + if (multi_read >= 2){ + if (err != 0){ // エラーが発生した場合 + for (i = 0; i < multi_read; i++) // 終了指示をだす + InterlockedExchange(&(th[i].loop), -1); + } + for (i = 0; i < multi_read; i++){ + if (hSub[i]){ + WaitForSingleObject(hSub[i], INFINITE); + GetExitCodeThread(hSub[i], &rv); + if ((err == 0) && (rv != 0)) + err = rv; + CloseHandle(hSub[i]); + } + } + } + if (err) + return err; + return off; +} + +// ハッシュ値を後で計算する +int set_common_packet_1pass( + unsigned char *buf, + int *packet_num, // 共通パケットの数 + int switch_u, // ユニコードのファイル名も記録する + file_ctx_c *files) +{ + char ascii_buf[MAX_LEN * 3]; + unsigned char set_id[16], *main_packet_buf; + unsigned char hash0[16] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, + 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e}; + wchar_t file_name[MAX_LEN], file_path[MAX_LEN]; + int i, off, num, len, data_size, main_packet_size; + unsigned int time_last; + __int64 prog_now = 0; + + wcscpy(file_path, base_dir); + + // 最初に Main packet を作成しておく + main_packet_size = 12 + (file_num * 16); + main_packet_buf = (unsigned char *)malloc(main_packet_size); + if (main_packet_buf == NULL){ + printf("malloc, %d\n", main_packet_size); + return 1; + } + memcpy(main_packet_buf, &block_size, 4); + memset(main_packet_buf + 4, 0, 4); + memcpy(main_packet_buf + 8, &entity_num, 4); + for (num = 0; num < file_num; num++) // 並び替えられた順序で File ID をコピーする + memcpy(main_packet_buf + (12 + (16 * num)), files[num].id, 16); + data_md5(main_packet_buf, main_packet_size, set_id); // Recovery Set ID を計算する + *packet_num = 1; + + // ファイルごとのパケットを作成する + time_last = GetTickCount(); + off = 0; + for (num = 0; num < file_num; num++){ + // File Description packet + memcpy(buf + (off + 64), files[num].id, 16); + memcpy(buf + (off + 64 + 32), files[num].hash, 16); + memcpy(buf + (off + 64 + 48), &(files[num].size), 8); + // ファイル名を UTF-8 に変換する + wcscpy(file_name, list_buf + files[num].name); + wcscpy(file_path + base_len, file_name); + unix_directory(file_name); // 記録時のディレクトリ記号は「/」にする + utf16_to_utf8(file_name, ascii_buf); + len = (int)strlen(ascii_buf); + for (i = len + 1; (i < len + 4) || (i < MAX_LEN * 3); i++) + ascii_buf[i] = 0; + len = (len + 3) & 0xFFFFFFFC; + memcpy(buf + (off + 64 + 56), ascii_buf, len); + data_size = 56 + len; + set_packet_header(buf + off, set_id, 2, data_size); // パケット・ヘッダーを作成する + if (files[num].size > 0){ + // ファイルの MD5-16k はもう不要なので、パケットの位置とサイズに変更する + //printf("File[%d], off = %d, size = %d\n", num, off, data_size); + memcpy(files[num].hash, &off, 4); // ハッシュ値の位置 = off + 64 + 16 + memcpy(files[num].hash + 4, &data_size, 4); + } else { + memcpy(buf + (off + 64 + 16), hash0, 16); + data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する + } + off += (64 + data_size); + (*packet_num)++; + + if (files[num].size > 0){ // Input File Slice Checksum packet + memcpy(buf + (off + 64), files[num].id, 16); + len = (int)((files[num].size + (__int64)block_size - 1) / block_size); + data_size = 16 + 20 * len; // パケット内容のサイズ + set_packet_header(buf + off, set_id, 3, data_size); // パケット・ヘッダーを作成する + // ファイルの MD5-16k はもう不要なので、パケットの位置とサイズに変更する + //printf("Checksum[%d], off = %d, size = %d\n", num, off, data_size); + memcpy(files[num].hash + 8, &off, 4); // チェックサムの位置 = off + 64 + 16 + memcpy(files[num].hash + 12, &data_size, 4); + //data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する + off += (64 + data_size); + (*packet_num)++; + } + + if (switch_u != 0){ // ユニコードのファイル名 + // 指定があってもファイル名が ASCII 文字だけならユニコードのパケットは作らない + len = (int)wcslen(file_name); + for (i = 0; i < len; i++){ + if (file_name[i] != ascii_buf[i]) + break; + } + if (i < len){ + // Unicode Filename packet + memcpy(buf + (off + 64), files[num].id, 16); + len = (int)wcslen(file_name) * 2; + memcpy(buf + (off + 64 + 16), file_name, len); + if (len & 3) + memset(buf + (off + 64 + 16 + len), 0, 2); + data_size = 16 + ((len + 3) & 0xFFFFFFFC); + set_packet_header(buf + off, set_id, 10, data_size); // パケット・ヘッダーを作成する + data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する + off += (64 + data_size); + (*packet_num)++; + } + } + } + + // Main packet + set_packet_header(buf + off, set_id, 1, main_packet_size); // パケット・ヘッダーを作成する + memcpy(buf + (off + 64), main_packet_buf, main_packet_size); + data_md5(buf + (off + 32), 32 + main_packet_size, buf + (off + 16)); // パケットの MD5 を計算する + off += (64 + main_packet_size); + + free(main_packet_buf); + return off; +} + +// パケットは作成済みなので、ハッシュ値だけ計算して追加する +int set_common_packet_hash( + unsigned char *buf, + file_ctx_c *files) +{ + unsigned char file_hash[16]; + int err, off, off2, num, data_size; + unsigned int time_last; + __int64 prog_now = 0; + +#ifdef TIMER +unsigned int time_start = GetTickCount(); +#endif + print_progress_text(0, "Computing file hash"); + + // ファイルごとのパケットを作成する + time_last = GetTickCount(); + for (num = 0; num < file_num; num++){ + if (files[num].size > 0){ + // Input File Slice Checksum packet + memcpy(&off, files[num].hash + 8, 4); // チェックサムの位置 = off + 64 + 16 + memcpy(&data_size, files[num].hash + 12, 4); + //printf("off = %d, data_size = %d\n", off, data_size); + // ファイルの MD5 ハッシュ値とブロックのチェックサムを同時に計算する + err = file_hash_crc(list_buf + files[num].name, files[num].size, file_hash, buf + (off + 64 + 16), &time_last, &prog_now); + if (err){ + if (err == 1) + printf_cp("file_hash_crc, %s\n", list_buf + files[num].name); + return err; + } + data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する + + // File Description packet + memcpy(&off2, files[num].hash, 4); // ハッシュ値の位置 = off + 64 + 16 + memcpy(&data_size, files[num].hash + 4, 4); + //printf("off2 = %d, data_size = %d\n", off2, data_size); + memcpy(buf + (off2 + 64 + 16), file_hash, 16); // 計算したハッシュ値をここで書き込む + data_md5(buf + (off2 + 32), 32 + data_size, buf + (off2 + 16)); // パケットの MD5 を計算する + + data_size = (int)((files[num].size + (__int64)block_size - 1) / block_size); + // ファイルの MD5-16k はもう不要なので、ブロックの CRC-32 に変更する + memset(files[num].hash, 0, 16); + for (off2 = 0; off2 < data_size; off2++){ // XOR して 16バイトに減らす + memcpy(&err, buf + (off + 64 + 16 + 20 * off2 + 16), 4); + ((unsigned int *)files[num].hash)[off2 & 3] ^= err; + } + } + } + print_progress_done(); // 改行して行の先頭に戻しておく + +#ifdef TIMER +time_start = GetTickCount() - time_start; +printf("hash %d.%03d sec\n", time_start / 1000, time_start % 1000); +#endif + return 0; +} + +// ソース・ファイルの情報から共通パケットのサイズを計算する +int measure_common_packet( + int *packet_num, // 共通パケットの数 + int switch_u) // ユニコードのファイル名も記録する +{ + char ascii_buf[MAX_LEN * 3]; + wchar_t *file_name, file_path[MAX_LEN]; + int i, list_off = 0, off = 0, len, data_size; + __int64 file_size; + WIN32_FILE_ATTRIBUTE_DATA AttrData; + + wcscpy(file_path, base_dir); + + // 最初に Main packet を作成しておく + data_size = 12 + (file_num * 16); + off += (64 + data_size); + *packet_num = 1; + + // Item index for debug output + if (split_size >= 4){ + printf("\n Size Slice Split : Filename\n"); + } else { + printf("\n Size Slice : Filename\n"); + } + + // ファイルごとのパケットを作成する + while (list_off < list_len){ + file_name = list_buf + list_off; + //list_off += (wcslen(file_name) + 1); + while (list_buf[list_off] != 0) + list_off++; + list_off++; + + // 属性を調べる + wcscpy(file_path + base_len, file_name); + if (!GetFileAttributesEx(file_path, GetFileExInfoStandard, &AttrData)){ + print_win32_err(); + printf_cp("GetFileAttributesEx, %s\n", file_name); + return 1; + } + if (AttrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ // フォルダなら + file_size = 0; + } else { // ファイルなら + file_size = ((__int64)AttrData.nFileSizeHigh << 32) | (unsigned __int64)AttrData.nFileSizeLow; + } + // Line for debug output + i = 0; + if (file_size > 0) + i = (int)((file_size + block_size - 1) / block_size); // ブロック数 + printf("%13I64d %6d", file_size, i); + if (split_size >= 4){ + i = (int)((file_size + split_size - 1) / split_size); // 分割数 + printf(" %5d", i); + } + printf_cp(" : \"%s\"\n", file_name); + + // File Description packet + // ファイル名を UTF-8 に変換する + utf16_to_utf8(file_name, ascii_buf); + len = (int)strlen(ascii_buf); + len = (len + 3) & 0xFFFFFFFC; + data_size = 56 + len; + off += (64 + data_size); + (*packet_num)++; + + if (file_size > 0){ // Input File Slice Checksum packet + data_size = 16; + data_size += (int)(((file_size + block_size - 1) / block_size) * 20); + off += (64 + data_size); + (*packet_num)++; + } + + if (switch_u != 0){ // ユニコードのファイル名 + // 指定があってもファイル名が ASCII 文字だけならユニコードのパケットは作らない + len = (int)wcslen(file_name); + for (i = 0; i < len; i++){ + if (file_name[i] != ascii_buf[i]){ + break; + } + } + if (i < len){ + // Unicode Filename packet + len = (int)wcslen(file_name) * 2; + data_size = 16 + ((len + 3) & 0xFFFFFFFC); + off += (64 + data_size); + (*packet_num)++; + } + } + } + + return off; +} + +// 末尾パケットを作成する +int set_footer_packet( + unsigned char *buf, + wchar_t *par_comment, // コメント + unsigned char *set_id) // Recovery Set ID +{ + unsigned char ascii_buf[COMMENT_LEN * 3]; + int off = 0, len, data_size; + + if (par_comment[0] != 0){ // ASCII と Unicode のどちらか一方だけを記録する + data_size = 0; + // 7-bit ASCII 文字以外が存在するかどうかを調べる + len = 0; + while (par_comment[len] != 0){ + if ((par_comment[len] & 0xFF80) != 0){ + data_size = 3; + break; + } + len++; + } + if (data_size == 0){ + // ASCII への変換に失敗した場合はユニコード・パケットを記録する + if (!WideCharToMultiByte(CP_ACP, 0, par_comment, -1, ascii_buf, COMMENT_LEN * 3, NULL, &len) || len){ + data_size = 1; + } else if (wcslen(par_comment) != strlen(ascii_buf)){ // 文字数とバイト数が一致しないなら + data_size = 2; + } + } + if (data_size == 0){ + // ASCII Comment packet + len = (int)strlen(ascii_buf); + memcpy(buf + (off + 64), ascii_buf, len); + if (len & 3) + memset(buf + (off + 64 + len), 0, 3); + data_size = (len + 3) & 0xFFFFFFFC; + set_packet_header(buf + off, set_id, 11, data_size); // パケット・ヘッダーを作成する + data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する + off += (64 + data_size); + } else { + // Unicode Comment packet + memset(buf + (off + 64), 0, 16); + len = (int)wcslen(par_comment) * 2; + memcpy(buf + (off + 64 + 16), par_comment, len); + if (len & 3) + memset(buf + (off + 64 + 16 + len), 0, 2); + data_size = 16 + ((len + 3) & 0xFFFFFFFC); + set_packet_header(buf + off, set_id, 12, data_size); // パケット・ヘッダーを作成する + data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する + off += (64 + data_size); + } + } + + // Creator packet + strcpy(ascii_buf, "par2j v" PRODUCT_VERSION); + len = (int)strlen(ascii_buf); + memcpy(buf + (off + 64), ascii_buf, len); + if (len & 3) + memset(buf + (off + 64 + len), 0, 3); + data_size = (len + 3) & 0xFFFFFFFC; + //strcpy(buf + (off + 64), "par2j v1.1.6\0"); + //data_size = 12; + set_packet_header(buf + off, set_id, 5, data_size); // パケット・ヘッダーを作成する + data_md5(buf + (off + 32), 32 + data_size, buf + (off + 16)); // パケットの MD5 を計算する + off += (64 + data_size); + + return off; +} + +// 末尾パケットのサイズを計算する +int measure_footer_packet( + wchar_t *par_comment) // コメント +{ + unsigned char ascii_buf[COMMENT_LEN * 3]; + int off = 0, len, data_size; + + if (par_comment[0] != 0){ // ASCII と Unicode のどちらか一方だけを記録する + data_size = 0; + // ASCII への変換に失敗した場合はユニコード・パケットを記録する + if (!WideCharToMultiByte(CP_ACP, 0, par_comment, -1, ascii_buf, COMMENT_LEN * 3, NULL, &len) || len){ + data_size = 1; + } else if (wcslen(par_comment) != strlen(ascii_buf)){ // 文字数とバイト数が一致しないなら + data_size = 2; + } + + if (data_size == 0){ + // ASCII Comment packet + len = (int)strlen(ascii_buf); + data_size = (len + 3) & 0xFFFFFFFC; + off += (64 + data_size); + } else { + // Unicode Comment packet + len = (int)wcslen(par_comment) * 2; + data_size = 16 + ((len + 3) & 0xFFFFFFFC); + off += (64 + data_size); + } + } + + // Creator packet + //len = strlen("par2j v*.*.*\0"); + //data_size = (len + 3) & 0xFFFFFFFC; + off += (64 + 12); + + return off; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ボリューム番号の桁数を求める +static int calc_max_num( + int block_distri, // パリティ・ブロックの分配方法 (3-bit目は番号の付け方) + int *max_num2) // 最大スライス個数の桁数 +{ + int i, j, num; + int exp_num, block_start, block_count, block_start_max, block_count_max = 0; + + if (block_distri >> 2){ // ファイル番号にする、vol_1, vol_2, vol_3, ... + i = recovery_num; + for (j = 1; j < 10; j++){ + i /= 10; + if (i == 0) + break; + } + return j; // ファイル数の桁数 + } + + block_start = 0; + block_count = 0; + exp_num = 1; + for (num = 0; num < recovery_num; num++){ + // リカバリ・ファイルのファイル名 + switch (block_distri & 3){ + case 0: // 同じ数なら + block_count = parity_num / recovery_num; + j = parity_num % recovery_num; // 割り切れない場合は + if ((j > 0) && (num < j)) + block_count++; + break; + case 1: // 倍々で異なる数にする + if (num == recovery_num - 1){ // 最後のファイルに余った分を入れる + block_count = parity_num - block_start; + } else { + exp_num = recovery_limit & 0xFFFF; + if (num >= 16){ + block_count = exp_num; + } else { + block_count = ((unsigned int)recovery_limit >> 16) << num; + if (block_count > exp_num) + block_count = exp_num; + } + } + break; + case 2: // 1,2,4,8,16 と2の乗数なら + block_count = exp_num; + if (block_count >= recovery_limit){ + block_count = recovery_limit; + } else { + exp_num *= 2; + } + break; + case 3: // 1,1,2,5,10,10,20,50 という decimal weights sizing scheme なら + block_count = exp_num; + if (block_count >= recovery_limit){ + block_count = recovery_limit; + } else { + switch (num % 4){ + case 1: + case 3: + exp_num = exp_num * 2; + break; + case 2: + exp_num = (exp_num / 2) * 5; + break; + } + } + break; + } + if (block_start + block_count > parity_num) + block_count = parity_num - block_start; + if (block_count_max < block_count) + block_count_max = block_count; + block_start += block_count; + } + block_start_max = first_num + block_start - block_count; // 最後に足した分を引く + for (j = 1; j < 10; j++){ + block_start_max /= 10; + if (block_start_max == 0) + break; + } + block_start_max = j; + for (j = 1; j < 10; j++){ + block_count_max /= 10; + if (block_count_max == 0) + break; + } + *max_num2 = j; + + return block_start_max; +} + +// ランダムに書き込むには管理者権限が必要 +//#define RANDOM_ACCESS + +// リカバリ・ファイルを作成して共通パケットをコピーする +int create_recovery_file( + 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, // 各リカバリ・ファイルのハンドル + parity_ctx_c *p_blk) // 各パリティ・ブロックの情報 +{ + wchar_t recovery_base[MAX_LEN], file_ext[EXT_LEN], *tmp_p; + int i, j, num; + int exp_num, block_start, block_count, block_start_max, block_count_max; + int repeat_max, packet_to, packet_from, packet_size, common_off; + unsigned int time_last, prog_num = 0; + __int64 file_off; +#ifdef RANDOM_ACCESS + HANDLE hToken; +#endif + +#ifdef TIMER +unsigned int time_start = GetTickCount(); +#endif + print_progress_text(0, "Constructing recovery file"); + time_last = GetTickCount(); + +#ifdef RANDOM_ACCESS + // SE_MANAGE_VOLUME_NAME 権限を有効にする + if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken) != 0){ + LUID luid; + if (LookupPrivilegeValue(NULL, SE_MANAGE_VOLUME_NAME, &luid ) != 0){ + TOKEN_PRIVILEGES tp; + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL); + if (GetLastError() != ERROR_SUCCESS){ + print_win32_err(); + } else { + printf("\nSE_MANAGE_VOLUME_NAME: ok\n"); + } + } + CloseHandle(hToken); + } +#endif + + // リカバリ・ファイルの拡張子には指定されたものを使う + file_ext[0] = 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); // 拡張子を記録しておく + *tmp_p = 0; // 拡張子を取り除く + } + } + + // ボリューム番号の桁数を求める + block_start_max = calc_max_num(block_distri, &block_count_max); + + // リカバリ・ファイルを作成して共通パケットを書き込む + block_start = 0; + block_count = 0; + exp_num = 1; + for (num = 0; num < recovery_num; num++){ + // リカバリ・ファイルのファイル名 + switch (block_distri & 3){ + case 0: // 同じ数なら + block_count = parity_num / recovery_num; + j = parity_num % recovery_num; // 割り切れない場合は + if ((j > 0) && (num < j)) + block_count++; + break; + case 1: // 倍々で異なる数にする + if (num == recovery_num - 1){ // 最後のファイルに余った分を入れる + block_count = parity_num - block_start; + } else { + exp_num = recovery_limit & 0xFFFF; + if (num >= 16){ + block_count = exp_num; + } else { + block_count = ((unsigned int)recovery_limit >> 16) << num; + if (block_count > exp_num) + block_count = exp_num; + } + } + break; + case 2: // 1,2,4,8,16 と2の乗数なら + block_count = exp_num; + if (block_count >= recovery_limit){ + block_count = recovery_limit; + } else { + exp_num *= 2; + } + break; + case 3: // 1,1,2,5,10,10,20,50 という decimal weights sizing scheme なら + block_count = exp_num; + if (block_count >= recovery_limit){ + block_count = recovery_limit; + } else { + switch (num % 4){ + case 1: + case 3: + exp_num = exp_num * 2; + break; + case 2: + exp_num = (exp_num / 2) * 5; + break; + } + } + break; + } + if (block_start + block_count > parity_num) + block_count = parity_num - block_start; + if (block_distri >> 2){ + // ファイル番号にする、vol_1, vol_2, vol_3, ... + swprintf(recovery_path, MAX_LEN, L"%s.vol_%0*d%s", recovery_base, block_start_max, num + 1, file_ext); + } else { + // QuickPar方式、volXX+YY + swprintf(recovery_path, MAX_LEN, L"%s.vol%0*d+%0*d%s", recovery_base, block_start_max, + first_num + block_start, block_count_max, block_count, file_ext); + } + // リカバリ・ファイルを開く + //move_away_file(recovery_path); // 既存のファイルをどかす + if (split_size == 1){ // 書庫ファイルに連結する場合は、後で読めるようにする + rcv_hFile[num] = CreateFile(recovery_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } else { // 書庫ファイルに連結しないので、読み込む必要が無い + rcv_hFile[num] = CreateFile(recovery_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } + if (rcv_hFile[num] == INVALID_HANDLE_VALUE){ + print_win32_err(); + rcv_hFile[num] = NULL; + printf_cp("cannot create file, %s\n", recovery_path); + return 1; + } + + // パケットの繰り返し回数を計算する + repeat_max = 1; + for (j = 2; j <= block_count; j *= 2) // 繰り返し回数は log2(block_count) + repeat_max++; + if ((packet_limit > 0) && (repeat_max > packet_limit)) + repeat_max = packet_limit; // 繰り返し回数を制限する + // リカバリ・ファイルの大きさを計算する + file_off = (__int64)(68 + block_size) * block_count; + file_off += (common_size * repeat_max) + footer_size; + // そのサイズにする + if (!SetFilePointerEx(rcv_hFile[num], *((PLARGE_INTEGER)&file_off), NULL, FILE_BEGIN)){ + print_win32_err(); + return 1; + } + if (!SetEndOfFile(rcv_hFile[num])){ + print_win32_err(); + return 1; + } +#ifdef RANDOM_ACCESS + if (!SetFileValidData(rcv_hFile[num], file_off)){ + print_win32_err(); + return 1; + } +#endif + + file_off = 0; + repeat_max *= packet_num; // リカバリ・ファイルの共通パケットの数 + packet_from = 0; + common_off = 0; + + // Recovery Slice packet は後から書き込む + for (j = block_start; j < block_start + block_count; j++){ + prog_num++; + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((prog_num * 1000) / parity_num)) + return 2; + time_last = GetTickCount(); + } + + // Recovery Slice packet + file_off += 68; + p_blk[j].file = num; + p_blk[j].off = file_off; // 開始位置だけ記録しておく + file_off += block_size; + + // どれだけの共通パケットを書き込むか + packet_size = 0; + packet_to = (int)((__int64)repeat_max * (j - block_start + 1) / block_count); + while (packet_to - packet_from > 0){ + memcpy(&i, common_buf + (common_off + (packet_size + 8)), 4); // そのパケットのデータ・サイズを調べる + packet_size += i; + packet_from++; + } + if (packet_size > 0){ // 共通パケットを書き込む + if (file_write_data(rcv_hFile[num], file_off, common_buf + common_off, packet_size)){ + printf_cp("file_write_data, %s\n", recovery_path); + return 1; + } + // オフセットが半分を超えたら戻しておく + common_off += packet_size; + if (common_off >= common_size) + common_off -= common_size; + file_off += packet_size; + } + } + block_start += block_count; + + // 末尾パケットを書き込む + if (file_write_data(rcv_hFile[num], file_off, footer_buf, footer_size)){ + printf_cp("file_write_data, %s\n", recovery_path); + return 1; + } + } + print_progress_done(); // 改行して行の先頭に戻しておく + +#ifdef TIMER +time_start = GetTickCount() - time_start; +printf("write %d.%03d sec\n", time_start / 1000, time_start % 1000); +#endif + + return 0; +} + +// パリティ・ブロックが完成した後に、encode_method5 から直接呼び出す +// リカバリ・ファイルを一個ずつ書き込んだ方が単純でいい! +// 各リカバリ・ファイルのハンドル、各パリティ・ブロックの情報、は不要になるし +int create_recovery_file_1pass( + wchar_t *recovery_base, + 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, // 各リカバリ・ファイルのハンドル + unsigned char *p_buf, // 計算済みのパリティ・ブロック + unsigned int unit_size) +{ + unsigned char *packet_header, hash[HASH_SIZE]; + wchar_t file_ext[EXT_LEN], *tmp_p; + int rv, j, num; + int exp_num, block_start, block_count, block_start_max, block_count_max; + int repeat_max, packet_to, packet_from, packet_size, common_off; + unsigned int time_last, prog_write; + __int64 prog_num, prog_base; + HANDLE hFile; + + // パリティ・ブロック計算に続いて経過表示する + prog_write = source_num >> 4; // 計算で 94%、書き込みで 6% ぐらい + if (prog_write == 0) + prog_write = 1; + prog_base = (__int64)(source_num + prog_write) * parity_num; // ブロックの合計掛け算個数 + prog_num = source_num * parity_num; // パリティ・ブロック計算が終了した直後から始める + time_last = GetTickCount(); + + if (recovery_path[MAX_LEN - 1] == 0){ // インデックス・ファイルを作るときだけ + // パリティ・ブロックを含まないリカバリ・ファイルを書き込む + //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); + return 1; + } + } + if (!WriteFile(hFile, common_buf, common_size, &rv, NULL)){ + print_win32_err(); + CloseHandle(hFile); + return 1; + } + if (!WriteFile(hFile, footer_buf, footer_size, &rv, NULL)){ + print_win32_err(); + CloseHandle(hFile); + return 1; + } + CloseHandle(hFile); + } + + // Recovery Slice packet 用のパケット・ヘッダーを作成しておく + packet_header = p_buf - 160; // ソース・ブロック領域を利用する + set_packet_header(packet_header, common_buf + 32, 4, block_size + 4); + + // リカバリ・ファイルの拡張子には指定されたものを使う + file_ext[0] = 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); // 拡張子を記録しておく + *tmp_p = 0; // 拡張子を取り除く + } + } + + // ボリューム番号の桁数を求める + block_start_max = calc_max_num(block_distri, &block_count_max); + + // リカバリ・ファイルを作成して共通パケットを書き込む + block_start = 0; + block_count = 0; + exp_num = 1; + for (num = 0; num < recovery_num; num++){ + // リカバリ・ファイルのファイル名 + switch (block_distri & 3){ + case 0: // 同じ数なら + block_count = parity_num / recovery_num; + j = parity_num % recovery_num; // 割り切れない場合は + if ((j > 0) && (num < j)) + block_count++; + break; + case 1: // 倍々で異なる数にする + if (num == recovery_num - 1){ // 最後のファイルに余った分を入れる + block_count = parity_num - block_start; + } else { + exp_num = recovery_limit & 0xFFFF; + if (num >= 16){ + block_count = exp_num; + } else { + block_count = ((unsigned int)recovery_limit >> 16) << num; + if (block_count > exp_num) + block_count = exp_num; + } + } + break; + case 2: // 1,2,4,8,16 と2の乗数なら + block_count = exp_num; + if (block_count >= recovery_limit){ + block_count = recovery_limit; + } else { + exp_num *= 2; + } + break; + case 3: // 1,1,2,5,10,10,20,50 という decimal weights sizing scheme なら + block_count = exp_num; + if (block_count >= recovery_limit){ + block_count = recovery_limit; + } else { + switch (num % 4){ + case 1: + case 3: + exp_num = exp_num * 2; + break; + case 2: + exp_num = (exp_num / 2) * 5; + break; + } + } + break; + } + if (block_start + block_count > parity_num) + block_count = parity_num - block_start; + if (block_distri >> 2){ + // ファイル番号にする、vol_1, vol_2, vol_3, ... + swprintf(recovery_path, MAX_LEN, L"%s.vol_%0*d%s", recovery_base, block_start_max, num + 1, file_ext); + } else { + // QuickPar方式、volXX+YY + swprintf(recovery_path, MAX_LEN, L"%s.vol%0*d+%0*d%s", recovery_base, block_start_max, + first_num + block_start, block_count_max, block_count, file_ext); + } + // リカバリ・ファイルを開く + //move_away_file(recovery_path); // 既存のファイルをどかす + if (rcv_hFile != NULL){ // 書庫ファイルに連結する場合は、後で読めるようにする + hFile = CreateFile(recovery_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } else { // 書庫ファイルに連結しないので、読み込む必要が無い + hFile = CreateFile(recovery_path, 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_path); + return 1; + } + if (rcv_hFile != NULL) + rcv_hFile[num] = hFile; // ファイル・ハンドルを記録する + + // パケットの繰り返し回数を計算する + repeat_max = 1; + for (j = 2; j <= block_count; j *= 2) // 繰り返し回数は log2(block_count) + repeat_max++; + if ((packet_limit > 0) && (repeat_max > packet_limit)) + repeat_max = packet_limit; // 繰り返し回数を制限する + + repeat_max *= packet_num; // リカバリ・ファイルの共通パケットの数 + packet_from = 0; + common_off = 0; + + // Recovery Slice packet は後から書き込む + for (j = block_start; j < block_start + block_count; j++){ + // パリティ・ブロックのチェックサムを検証する + checksum16_return(p_buf, hash, unit_size - HASH_SIZE); + if (memcmp(p_buf + unit_size - HASH_SIZE, hash, HASH_SIZE) != 0){ + printf("checksum mismatch, recovery slice %d\n", first_num + j); + if (rcv_hFile == NULL) + CloseHandle(hFile); + return 1; + } + + // Recovery Slice packet + memcpy(p_buf - 68, packet_header, 64); // パケット・ヘッダーをコピーする + rv = first_num + j; // 最初のパリティ・ブロック番号の分だけ足す + memcpy(p_buf - 4, &rv, 4); // Recovery Slice の番号を書き込む + data_md5(p_buf - 36, 36 + block_size, p_buf - 52); // パケットの MD5 を計算する + if (!WriteFile(hFile, p_buf - 68, 68 + block_size, &rv, NULL)){ + print_win32_err(); + printf("file_write_data, recovery slice %d\n", first_num + j); + if (rcv_hFile == NULL) + CloseHandle(hFile); + return 1; + } + p_buf += unit_size; + + // 経過表示 + prog_num += prog_write; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((int)((prog_num * 1000) / prog_base))){ + if (rcv_hFile == NULL) + CloseHandle(hFile); + return 2; + } + time_last = GetTickCount(); + } + + // どれだけの共通パケットを書き込むか + packet_size = 0; + packet_to = (int)((__int64)repeat_max * (j - block_start + 1) / block_count); + while (packet_to - packet_from > 0){ + memcpy(&rv, common_buf + (common_off + (packet_size + 8)), 4); // そのパケットのデータ・サイズを調べる + packet_size += rv; + packet_from++; + } + if (packet_size > 0){ // 共通パケットを書き込む + if (!WriteFile(hFile, common_buf + common_off, packet_size, &rv, NULL)){ + print_win32_err(); + if (rcv_hFile == NULL) + CloseHandle(hFile); + return 1; + } + // オフセットが半分を超えたら戻しておく + common_off += packet_size; + if (common_off >= common_size) + common_off -= common_size; + } + } + block_start += block_count; + + // 末尾パケットを書き込む + if (!WriteFile(hFile, footer_buf, footer_size, &rv, NULL)){ + print_win32_err(); + if (rcv_hFile == NULL) + CloseHandle(hFile); + return 1; + } + if (rcv_hFile == NULL){ // 後でファイル・ハンドルが必要なければ + //FlushFileBuffers(hFile); // 書き込みが完了するのを待つ? + // 今まで問題視されなかった訳だし、特に気にしなくていいかも・・・ + CloseHandle(hFile); // ファイルを閉じる + } + } + print_progress_done(); // 改行して行の先頭に戻しておく + //printf("prog_num = %I64d / %I64d\n", prog_num, prog_base); + + return 0; +} + +// 作成中のリカバリ・ファイルを削除する +void delete_recovery_file( + wchar_t *recovery_path, // 作業用 + int block_distri, // パリティ・ブロックの分配方法 (3-bit目は番号の付け方) + int switch_p, // インデックス・ファイルを作らない + HANDLE *rcv_hFile) // 各リカバリ・ファイルのハンドル +{ + wchar_t recovery_base[MAX_LEN], file_ext[EXT_LEN], *tmp_p; + int j, num; + int exp_num, block_start, block_count, block_start_max, block_count_max; + + if (switch_p == 0) + DeleteFile(recovery_file); // インデックス・ファイルを削除する + + // リカバリ・ファイルの拡張子には指定されたものを使う + file_ext[0] = 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); // 拡張子を記録しておく + *tmp_p = 0; // 拡張子を取り除く + } + } + + // ボリューム番号の桁数を求める + block_start_max = calc_max_num(block_distri, &block_count_max); + + // リカバリ・ファイルを削除する + block_start = 0; + block_count = 0; + exp_num = 1; + for (num = 0; num < recovery_num; num++){ + // リカバリ・ファイルのファイル名 + switch (block_distri & 3){ + case 0: // 同じ数なら + block_count = parity_num / recovery_num; + j = parity_num % recovery_num; // 割り切れない場合は + if ((j > 0) && (num < j)) + block_count++; + break; + case 1: // 倍々で異なる数にする + if (num == recovery_num - 1){ // 最後のファイルに余った分を入れる + block_count = parity_num - block_start; + } else { + exp_num = recovery_limit & 0xFFFF; + if (num >= 16){ + block_count = exp_num; + } else { + block_count = ((unsigned int)recovery_limit >> 16) << num; + if (block_count > exp_num) + block_count = exp_num; + } + } + break; + case 2: // 1,2,4,8,16 と2の乗数なら + block_count = exp_num; + if (block_count >= recovery_limit){ + block_count = recovery_limit; + } else { + exp_num *= 2; + } + break; + case 3: // 1,1,2,5,10,10,20,50 という decimal weights sizing scheme なら + block_count = exp_num; + if (block_count >= recovery_limit){ + block_count = recovery_limit; + } else { + switch (num % 4){ + case 1: + case 3: + exp_num = exp_num * 2; + break; + case 2: + exp_num = (exp_num / 2) * 5; + break; + } + } + break; + } + if (block_start + block_count > parity_num) + block_count = parity_num - block_start; + if (block_distri >> 2){ + // ファイル番号にする、vol_1, vol_2, vol_3, ... + swprintf(recovery_path, MAX_LEN, L"%s.vol_%0*d%s", recovery_base, block_start_max, num + 1, file_ext); + } else { + // QuickPar方式、volXX+YY + swprintf(recovery_path, MAX_LEN, L"%s.vol%0*d+%0*d%s", recovery_base, block_start_max, + first_num + block_start, block_count_max, block_count, file_ext); + } + block_start += block_count; + + if (rcv_hFile != NULL){ + if (rcv_hFile[num] != NULL){ // ファイルが作成済みなら (開いていれば) + CloseHandle(rcv_hFile[num]); + rcv_hFile[num] = NULL; + DeleteFile(recovery_path); // 途中までのリカバリ・ファイルを削除する + } + } else { + DeleteFile(recovery_path); // 作成したリカバリ・ファイルを削除する + } + } +} + +// リカバリ・ファイルのサイズを計算する +void measure_recovery_file( + wchar_t *recovery_path, // 作業用 (最初はコメントが入ってる) + int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限 + int block_distri, // パリティ・ブロックの分配方法 + int packet_num, // 共通パケットの数 + int common_size, // 共通パケットのバッファー・サイズ + int footer_size, // 末尾パケットのバッファー・サイズ + int switch_p) // インデックス・ファイルを作らない +{ + char ascii_buf[MAX_LEN * 3]; + wchar_t recovery_base[MAX_LEN], file_ext[EXT_LEN], *tmp_p; + int j, num; + int exp_num, block_start, block_count, block_start_max, block_count_max; + int footer_num, packet_count, repeat_max; + __int64 file_size; + + get_file_name(recovery_file, recovery_base); // ファイル名だけにする + footer_num = 1; + if (recovery_path[0] != 0) + footer_num++; + + total_file_size = 0; // リカバリ・ファイルの合計サイズを計算する + if (switch_p == 0){ + // Index File を先に表示する + file_size = common_size + footer_size; + total_file_size += file_size; + packet_count = packet_num + footer_num; + utf16_to_cp(recovery_base, ascii_buf, cp_output); + printf("%13I64d %7d 0 : \"%s\"\n", file_size, packet_count, ascii_buf); + } + if (parity_num == 0) // パリティ・ブロックを作らない場合はここで終わる + return; + + // リカバリ・ファイルの拡張子には指定されたものを使う + file_ext[0] = 0; + 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); // 拡張子を記録しておく + *tmp_p = 0; // 拡張子を取り除く + } + } + + // ボリューム番号の桁数を求める + block_start_max = calc_max_num(block_distri, &block_count_max); + + // リカバリ・ファイルを作成して共通パケットを書き込む + block_start = 0; + block_count = 0; + exp_num = 1; + for (num = 0; num < recovery_num; num++){ + // リカバリ・ファイルのファイル名 + switch (block_distri & 3){ + case 0: // 同じ数なら + block_count = parity_num / recovery_num; + j = parity_num % recovery_num; // 割り切れない場合は + if ((j > 0) && (num < j)) + block_count++; + break; + case 1: // 倍々で異なる数にする + if (num == recovery_num - 1){ // 最後のファイルに余った分を入れる + block_count = parity_num - block_start; + } else { + exp_num = recovery_limit & 0xFFFF; + if (num >= 16){ + block_count = exp_num; + } else { + block_count = ((unsigned int)recovery_limit >> 16) << num; + if (block_count > exp_num) + block_count = exp_num; + } + } + break; + case 2: // 1,2,4,8,16 と2の乗数なら + block_count = exp_num; + if (block_count >= recovery_limit){ + block_count = recovery_limit; + } else { + exp_num *= 2; + } + break; + case 3: // 1,1,2,5,10,10,20,50 という decimal weights sizing scheme なら + block_count = exp_num; + if (block_count >= recovery_limit){ + block_count = recovery_limit; + } else { + switch (num % 4){ + case 1: + case 3: + exp_num = exp_num * 2; + break; + case 2: + exp_num = (exp_num / 2) * 5; + break; + } + } + break; + } + if (block_start + block_count > parity_num) + block_count = parity_num - block_start; + if (block_distri >> 2){ + // ファイル番号にする、vol_1, vol_2, vol_3, ... + swprintf(recovery_path, MAX_LEN, L"%s.vol_%0*d%s", recovery_base, block_start_max, num + 1, file_ext); + } else { + // QuickPar方式、volXX+YY + swprintf(recovery_path, MAX_LEN, L"%s.vol%0*d+%0*d%s", recovery_base, block_start_max, + first_num + block_start, block_count_max, block_count, file_ext); + } + utf16_to_cp(recovery_path, ascii_buf, cp_output); + + // パケットの繰り返し回数を計算する + repeat_max = 1; + for (j = 2; j <= block_count; j *= 2) // 繰り返し回数は log2(block_count) + repeat_max++; + if ((packet_limit > 0) && (repeat_max > packet_limit)) + repeat_max = packet_limit; // 繰り返し回数を制限する + // リカバリ・ファイルの大きさを計算する + file_size = (__int64)(68 + block_size) * block_count; + file_size += (common_size * repeat_max) + footer_size; + total_file_size += file_size; + packet_count = repeat_max * packet_num; // リカバリ・ファイルの共通パケットの数 + packet_count += footer_num + block_count; + printf("%13I64d %7d %6d : \"%s\"\n", file_size, packet_count, block_count, ascii_buf); + + block_start += block_count; + } +} + +// ソース・ファイルを分割する (分割サイズは split_size で指定する) +int split_files( + file_ctx_c *files, + int *cur_num, int *cur_id) // エラー発生時は、その時点でのファイル番号と分割番号が戻る +{ + unsigned char buf[IO_SIZE]; + wchar_t file_path[MAX_LEN], recovery_dir[MAX_LEN]; + wchar_t *file_name, *file_ext; + int num, id, split_num, split_max, total_num, dir_len; + int name_len, ext_len, num2; + unsigned int rv, len, split_left; + unsigned int time_last, prog_num = 0; + __int64 num8, file_left; + HANDLE hFile, hFile_src; + + // 分割したファイルをどこに保存するか + get_base_dir(recovery_file, recovery_dir); + if (compare_directory(base_dir, recovery_dir) == 0){ // 分割先が同じ場所ならコピーしない + recovery_dir[0] = 0; + dir_len = base_len; + } else { + dir_len = (int)wcslen(recovery_dir); + //printf_cp("\n save_path = %s \n", recovery_dir); + } + + // うまく分割できるか確かめる + total_num = 0; + for (num = 0; num < file_num; num++){ + if (files[num].size > (__int64)split_size){ + num8 = (files[num].size + (__int64)split_size - 1) / split_size; + if (num8 > 99999){ // 分割個数が 99999を超えたらエラー + printf("too many split file, %d\n", num); + *cur_num = -1; + *cur_id = 0; + return 1; + } + total_num += (int)num8; + } else if (recovery_dir[0] != 0){ // 分割先が異なる場合だけコピーする + total_num++; + } + } + //printf("split total num = %d\n", total_num); + if (total_num == 0) + return 0; // 分割する必要なし + + // 分割したファイルが、ソース・ファイルを上書きしないかチェックする + for (num = 0; num < file_num; num++){ + // 拡張子を捜す + ext_len = 0; + file_name = list_buf + files[num].name; + file_ext = wcsrchr(file_name, '.'); + if (file_ext != NULL){ + name_len = (int)(file_ext - file_name); + ext_len = (int)wcslen(file_ext); + if ((ext_len >= 4) && (ext_len <= 6)){ // 最長で ".99999" になる + split_num = 0; + for (id = 1; id < ext_len; id++){ + if ((file_ext[id] < '0') || (file_ext[id] > '9')){ + ext_len = 0; + break; + } + split_num = split_num * 10 + (file_ext[id] - '0'); + } + // 分割先が異なる場合は、分割されるソース・ファイルを上書きしない + if ((ext_len > 0) && (recovery_dir[0] != 0) && (files[num].size > split_size)) + ext_len = 0; + } else { + ext_len = 0; + } + } + if (ext_len > 0){ // 全て数字の拡張子を持つソース・ファイルがあるなら + //printf_cp("\n risky name = %s \n", file_name); + for (num2 = 0; num2 < file_num; num2++){ + if (num2 == num) + continue; + if (_wcsnicmp(list_buf + files[num2].name, file_name, name_len) == 0){ + //printf_cp(" match name = %s \n", list_buf + files[num2].name); + num8 = (files[num2].size + (__int64)split_size - 1) / split_size; + split_max = (int)num8; + //printf("split_num = %d, split max = %d, ext_len = %d \n", split_num, split_max, ext_len); + // 分割数的に問題なければ除外する + if ((split_max == 1) || (split_num > split_max)) + continue; // 分割サイズよりも小さなファイルは、数字の拡張子が付かない + if (((split_max < 1000) && (ext_len >= 5)) || ((split_max < 10000) && (ext_len >= 6))) + continue; // 拡張子の桁数が異なる + // 上書きする危険性があるのでエラーにする + printf_cp("split bad file, %s\n", file_name); + *cur_num = -1; + *cur_id = 0; + return 1; + } + } + } + } + + // 分割サイズよりも大きなファイルだけ分割する + printf("\n"); + print_progress_text(0, "Spliting file"); + time_last = GetTickCount(); + for (num = 0; num < file_num; num++){ + if (files[num].size > (__int64)split_size){ // 分割する + file_left = files[num].size; + num8 = (file_left + (__int64)split_size - 1) / split_size; + split_num = (int)num8; + if (split_num <= 999){ + split_max = 3; // 標準では 001~999 の 999分割までだから 3桁 + } else if (split_num <= 9999){ + split_max = 4; + } else { // ソース・ブロックは 32768個以下だから分割数もそれ以下のはず + split_max = 5; + } + + // ソース・ファイルを開く + wcscpy(file_path, base_dir); + wcscpy(file_path + base_len, list_buf + files[num].name); + hFile_src = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile_src == INVALID_HANDLE_VALUE){ + print_win32_err(); + printf_cp("cannot open file, %s\n", list_buf + files[num].name); + *cur_num = num; + *cur_id = 0; + return 1; + } + if (recovery_dir[0] != 0) + wcscpy(file_path, recovery_dir); // 異なる場所に保存する + for (id = 1; id <= split_num; id++){ + swprintf(file_path + dir_len, _countof(file_path) - dir_len, L"%s.%0*d", list_buf + files[num].name, split_max, id); + hFile = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + CloseHandle(hFile_src); + printf_cp("cannot create file, %s\n", file_path); + *cur_num = num; + *cur_id = id - 1; + return 1; + } + // ファイルを分割サイズごとに書き込んでいく + split_left = split_size; + if ((__int64)split_left > file_left) + split_left = (unsigned int)file_left; + file_left -= split_left; + while (split_left){ + len = IO_SIZE; + if (split_left < IO_SIZE) + len = split_left; + split_left -= len; + if (!ReadFile(hFile_src, buf, len, &rv, NULL) || (len != rv)){ + print_win32_err(); + CloseHandle(hFile_src); + CloseHandle(hFile); + printf("ReadFile, input file %d\n", num); + *cur_num = num; + *cur_id = id; + return 1; + } + if (!WriteFile(hFile, buf, len, &rv, NULL)){ + print_win32_err(); + CloseHandle(hFile_src); + CloseHandle(hFile); + printf("WriteFile, split file %d.%03d", num, id); + *cur_num = num; + *cur_id = id; + return 1; + } + } + CloseHandle(hFile); + + // 経過表示 + prog_num++; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((prog_num * 1000) / total_num)){ + CloseHandle(hFile_src); + *cur_num = num; + *cur_id = id; + return 2; + } + time_last = GetTickCount(); + } + } + CloseHandle(hFile_src); + + } else if (recovery_dir[0] != 0){ // 小さなファイルはそのままコピーする + // ソース・ファイルのパス + wcscpy(file_path, base_dir); + wcscpy(file_path + base_len, list_buf + files[num].name); + // コピー先のパス + wcscpy(recovery_dir + dir_len, list_buf + files[num].name); + rv = CopyFile(file_path, recovery_dir, FALSE); + if (rv != 0){ // 読み取り専用属性を解除しておく + len = GetFileAttributes(recovery_dir); + if ((len & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY)) != 0){ + len &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY); + rv = SetFileAttributes(recovery_dir, len); + } + } + if (rv == 0){ + print_win32_err(); + printf_cp("cannot copy file, %s\n", recovery_dir); + *cur_num = num; + *cur_id = 0; + return 1; + } + recovery_dir[dir_len] = 0; // ディレクトリに戻しておく + + // 経過表示 + prog_num++; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((prog_num * 1000) / total_num)){ + *cur_num = num; + *cur_id = 0; + return 2; + } + time_last = GetTickCount(); + } + } + } + print_progress_done(); // 改行して行の先頭に戻しておく + + return 0; +} + +// 分割されたソース・ファイルを削除する +void delete_split_files( + file_ctx_c *files, + int max_num, int max_id) // エラー発生時のファイル番号と分割番号 +{ + wchar_t file_path[MAX_LEN], recovery_dir[MAX_LEN]; + int num, id, split_num, split_max, dir_len; + __int64 num8, file_left; + + // 分割したファイルをどこに保存するか + get_base_dir(recovery_file, recovery_dir); + if (compare_directory(base_dir, recovery_dir) == 0){ // 分割先が同じ場所ならコピーしない + recovery_dir[0] = 0; + dir_len = base_len; + } else { + dir_len = (int)wcslen(recovery_dir); + } + + // 分割サイズよりも大きなファイルだけ分割する + for (num = 0; num <= max_num; num++){ + if (files[num].size > (__int64)split_size){ // 分割されたファイルを削除する + file_left = files[num].size; + num8 = (file_left + (__int64)split_size - 1) / split_size; + split_num = (int)num8; // 分割個数は 99999 以下のはず + if (split_num <= 999){ + split_max = 3; // 標準では 001~999 の 999分割までだから 3桁 + } else if (split_num <= 9999){ + split_max = 4; + } else { // if (split_num <= 99999){ + split_max = 5; + } + if (num == max_num) + split_num = max_id; // 中断した所まで削除する + for (id = 1; id <= split_num; id++){ + if (recovery_dir[0] != 0){ + wcscpy(file_path, recovery_dir); // 異なる場所に保存した + } else { + wcscpy(file_path, base_dir); + } + swprintf(file_path + dir_len, _countof(file_path) - dir_len, L"%s.%0*d", list_buf + files[num].name, split_max, id); + if (!DeleteFile(file_path)) // 既に分割してるソース・ファイルを削除する + return; // 削除に失敗したら出る + } + + } else if (recovery_dir[0] != 0){ // 別の場所にコピーした場合だけ削除する + wcscpy(recovery_dir + dir_len, list_buf + files[num].name); // コピー先のパス + if (!DeleteFile(recovery_dir)) // コピーしたソース・ファイルを削除する + return; // 削除に失敗したら出る + recovery_dir[dir_len] = 0; // ディレクトリに戻しておく + } + } +} + +#define ZIP_SEARCH_SIZE 1022 + +// リカバリ・ファイルをソース・ファイルの末尾にくっつける +// フォーマットによってはソース・ファイルが使えなくなる恐れがあるので、制限する? +// 前方から読み込んでいくストリーム形式だと問題ないが、フッターを先に読む形式だと無理 +// 7z 書庫は問題ないが、ZIP 書庫は末尾を工夫しないといけない。 +// bz2 と gz は 7-Zip で展開時にエラーが表示される・・・ +// EXE や DLL ファイルはチェックサムが変わるので、読み込みエラーが発生するかも。 +// JPEG, GIF, PNG, MP3 などは問題ないっぽいけど、リカバリ・レコードを埋め込む必要が無い。 +int append_recovery_file( + wchar_t *file_name, // ソース・ファイルの名前 + HANDLE *rcv_hFile, // 各リカバリ・ファイルのハンドル + int no_index) // インデックス・ファイルが存在しない +{ + unsigned char buf[IO_SIZE], header[ZIP_SEARCH_SIZE]; + wchar_t file_path[MAX_LEN], save_path[MAX_LEN]; + int num, header_size, header_off; + unsigned int rv, len, attach_flag; + unsigned int time_last; + HANDLE hFileWrite = NULL, hFileRead = NULL; + LARGE_INTEGER qwi, orig_size; // Quad Word Integer + + // 結合したファイルをどこに保存するか + attach_flag = 0; + printf("\n"); + print_progress_text(0, "Appending recovery record"); + time_last = GetTickCount(); + get_base_dir(recovery_file, save_path); + if (compare_directory(base_dir, save_path) != 0){ // 結合先が異なる場所ならコピーする + wcscpy(file_path, base_dir); + wcscat(file_path, file_name); + wcscat(save_path, file_name); + if (CopyFile(file_path, save_path, FALSE) == 0){ + print_win32_err(); + printf_cp("cannot copy file, %s\n", save_path); + return 1; + } + attach_flag = 0x02000000; // コピーした印 + } else { + wcscpy(save_path, base_dir); + wcscat(save_path, file_name); + attach_flag = 0x01000000; // ソース・ファイルが同じ場所の印 + } + // 読み取り専用属性を解除しておく + len = GetFileAttributes(save_path); + if ((len & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY)) != 0){ + len &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY); + SetFileAttributes(save_path, len); + } + + // ソース・ファイルを開く + hFileWrite = CreateFile(save_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hFileWrite == INVALID_HANDLE_VALUE){ + print_win32_err(); + printf_cp("cannot open file, %s\n", save_path); + return 1; + } + // 対応するフォーマットであることを確認する + // 7z = 先頭 6-byte の Signature で判定する + // zip = 先頭 4-byte の local file header signature と + // 末尾 22-byte の end of central directory record で判定する + if (!ReadFile(hFileWrite, buf, 28, &len, NULL) || (len != 28)){ + print_win32_err(); + goto error_end; + } + if (((unsigned int *)buf)[0] == 0x04034b50){ + // 末尾を何バイト読み込むか(ZIPのコメントに対応する) + if (total_file_size < ZIP_SEARCH_SIZE){ + header_size = (int)total_file_size - 32; // 最低でも 32 + 22 = 54 バイトはあるはず + } else { + header_size = ZIP_SEARCH_SIZE; + } + // ファイルの末尾 header_size バイトを読み込む + qwi.QuadPart = - header_size; + if (!SetFilePointerEx(hFileWrite, qwi, NULL, FILE_END)){ + print_win32_err(); + goto error_end; + } + if (!ReadFile(hFileWrite, header, header_size, &len, NULL) || (len != header_size)){ + print_win32_err(); + goto error_end; + } + // 後ろから Signature を探す + num = header_size - 22; + while (num >= 0){ + if (((unsigned int *)(header + num))[0] == 0x06054b50){ + memcpy(&len, header + num + 12, 4); // ヘッダーに記録されてるサイズを確認する + memcpy(&rv, header + num + 16, 4); + if (rv + len + header_size - num == total_file_size){ + attach_flag |= 0x10000000; + header_off = num; + header_size -= num; + break; + } + } else if (((unsigned int *)(header + num))[0] == 0x06064b50){ + memcpy(&len, header + num + 40, 4); // ZIP64ヘッダーに記録されてるサイズを確認する + memcpy(&orig_size.QuadPart, header + num + 48, 8); + if (len + orig_size.QuadPart + header_size - num == total_file_size){ + attach_flag |= 0x10000000; + header_off = num; + header_size -= num; + break; + } + } + num--; + } + } else if ((((unsigned short *)buf)[0] == 0x7A37) && (((unsigned int *)(buf + 2))[0] == 0x1C27AFBC)){ + __int64 size1, size2; + memcpy(&size1, buf + 12, 8); // ヘッダーに記録されてるサイズを確認する + memcpy(&size2, buf + 20, 8); + if (32 + size1 + size2 == total_file_size) + attach_flag |= 0x20000000; + } + if ((attach_flag & 0x30000000) == 0){ + printf("\ninvalid archive format\n"); + goto error_end; + } + + // ファイルの位置を末尾にする + qwi.QuadPart = 0; + if (!SetFilePointerEx(hFileWrite, qwi, &orig_size, FILE_END)){ + print_win32_err(); + goto error_end; + } + //printf("\n qwi.QuadPart = %I64d\n", orig_size.QuadPart); + + if (no_index == 0){ // インデックス・ファイルを開く + hFileRead = CreateFile(recovery_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFileRead == INVALID_HANDLE_VALUE){ + print_win32_err(); + printf_cp("cannot open file, %s\n", recovery_file); + goto error_end; + } + + // 末尾に追加していく + do { + if (!ReadFile(hFileRead, buf, IO_SIZE, &len, NULL)){ + print_win32_err(); + goto error_end; + } + if (len == 0) + break; + attach_flag |= len; + if (!WriteFile(hFileWrite, buf, len, &rv, NULL)){ + print_win32_err(); + goto error_end; + } + } while (len > 0); + CloseHandle(hFileRead); + hFileRead = NULL; + } + + // リカバリ・ファイルを開く + for (num = 0; num < recovery_num; num++){ + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress(((num + 1) * 1000) / recovery_num)){ + goto error_end; + } + time_last = GetTickCount(); + } + + // ファイルの位置を先頭にする + qwi.QuadPart = 0; + if (!SetFilePointerEx(rcv_hFile[num], qwi, NULL, FILE_BEGIN)){ + print_win32_err(); + goto error_end; + } + + // 末尾に追加していく + do { + if (!ReadFile(rcv_hFile[num], buf, IO_SIZE, &len, NULL)){ + print_win32_err(); + goto error_end; + } + if (len == 0) + break; + attach_flag |= len; + if (!WriteFile(hFileWrite, buf, len, &rv, NULL)){ + print_win32_err(); + goto error_end; + } + } while (len > 0); + } + + // ZIP 書庫なら末尾にヘッダーを追加する + if (attach_flag & 0x10000000){ + if (!WriteFile(hFileWrite, header + header_off, header_size, &rv, NULL) || (rv != header_size)){ + print_win32_err(); + goto error_end; + } + } + + print_progress_done(); // 改行して行の先頭に戻しておく + CloseHandle(hFileWrite); + return 0; + +error_end: // エラー発生時 + if (attach_flag & 0x02000000){ // コピーしたファイルを削除する + if (hFileWrite != NULL){ + CloseHandle(hFileWrite); + hFileWrite = NULL; + } + DeleteFile(save_path); + } else if ((attach_flag & 0x01FFFFFF) > 0x01000000){ // 追加したバイトを取り除く + if (SetFilePointerEx(hFileWrite, orig_size, NULL, FILE_BEGIN) != 0) + SetEndOfFile(hFileWrite); + } + if (hFileWrite != NULL) + CloseHandle(hFileWrite); + if (hFileRead != NULL) + CloseHandle(hFileRead); + return 1; +} + diff --git a/source/par2j/create.h b/source/par2j/create.h new file mode 100644 index 0000000..f434950 --- /dev/null +++ b/source/par2j/create.h @@ -0,0 +1,125 @@ +#ifndef _CREATE_H_ +#define _CREATE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +// PAR 2.0 のパケット・ヘッダーを作成する +void set_packet_header( + unsigned char *buf, // ヘッダーを格納する 64バイトのバッファー + unsigned char *set_id, // Recovery Set ID、16バイト + int type_num, // そのパケットの型番号 + unsigned int body_size); // パケットのデータ・サイズ + +// ソース・ファイルの情報を集める +int get_source_files(file_ctx_c *files); + +// 共通パケットを作成する +int set_common_packet( + unsigned char *buf, + int *packet_num, // 共通パケットの数 + int switch_u, // ユニコードのファイル名も記録する + file_ctx_c *files); + +// 複数ファイルのハッシュ値を同時に求めるバージョン +int set_common_packet_multi( + unsigned char *buf, + int *packet_num, // 共通パケットの数 + int switch_u, // ユニコードのファイル名も記録する + file_ctx_c *files); + +// ハッシュ値を後で計算する +int set_common_packet_1pass( + unsigned char *buf, + int *packet_num, // 共通パケットの数 + int switch_u, // ユニコードのファイル名も記録する + file_ctx_c *files); + +// パケットは作成済みなので、ハッシュ値だけ計算して追加する +int set_common_packet_hash( + unsigned char *buf, + file_ctx_c *files); + +// ソース・ファイルの情報から共通パケットのサイズを計算する +int measure_common_packet( + int *packet_num, // 共通パケットの数 + int switch_u); // ユニコードのファイル名も記録する + +// 末尾パケットを作成する +int set_footer_packet( + unsigned char *buf, + wchar_t *par_comment, // コメント + unsigned char *set_id); // Recovery Set ID + +// 末尾パケットのサイズを計算する +int measure_footer_packet( + wchar_t *par_comment); // コメント + +// リカバリ・ファイルを作成して共通パケットをコピーする +int create_recovery_file( + wchar_t *recovery_path, // 作業用 + int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限 + int block_distri, // パリティ・ブロックの分配方法 + int packet_num, // 共通パケットの数 + unsigned char *common_buf, // 共通パケットのバッファー + int common_size, // 共通パケットのバッファー・サイズ + unsigned char *footer_buf, // 末尾パケットのバッファー + int footer_size, // 末尾パケットのバッファー・サイズ + HANDLE *rcv_hFile, // 各リカバリ・ファイルのハンドル + parity_ctx_c *p_blk); // 各パリティ・ブロックの情報 + +int create_recovery_file_1pass( + wchar_t *recovery_base, + 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, // 各リカバリ・ファイルのハンドル + unsigned char *p_buf, // 計算済みのパリティ・ブロック + unsigned int unit_size); + +// 作成中のリカバリ・ファイルを削除する +void delete_recovery_file( + wchar_t *recovery_path, // 作業用 + int block_distri, // パリティ・ブロックの分配方法 + int switch_p, // インデックス・ファイルを作らない + HANDLE *rcv_hFile); // 各リカバリ・ファイルのハンドル + +// リカバリ・ファイルのサイズを計算する +void measure_recovery_file( + wchar_t *recovery_path, // 作業用 (最初はコメントが入ってる) + int packet_limit, // リカバリ・ファイルのパケット繰り返しの制限 + int block_distri, // パリティ・ブロックの分配方法 + int packet_num, // 共通パケットの数 + int common_size, // 共通パケットのバッファー・サイズ + int footer_size, // 末尾パケットのバッファー・サイズ + int switch_p); // インデックス・ファイルを作らない + +// ソース・ファイルを分割する +int split_files( + file_ctx_c *files, + int *cur_num, int *cur_id); // エラー発生時は、その時点でのファイル番号と分割番号が戻る + +// 分割されたソース・ファイルを削除する +void delete_split_files( + file_ctx_c *files, + int max_num, int max_id); // エラー発生時のファイル番号と分割番号 + +// リカバリ・ファイルをソース・ファイルの末尾にくっつける +int append_recovery_file( + wchar_t *file_name, // ソース・ファイルの名前 + HANDLE *rcv_hFile, // 各リカバリ・ファイルのハンドル + int no_index); // インデックス・ファイルが存在しない + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par2j/gf16.c b/source/par2j/gf16.c new file mode 100644 index 0000000..b4b5bef --- /dev/null +++ b/source/par2j/gf16.c @@ -0,0 +1,2749 @@ +// The basic of 16-bit Galois Field arithmetic is based on Galois.c by James S. Plank. +// Modified by Yutaka Sawada to support MMX, SSE2, and SSSE3. + +/* Galois.c + * James S. Plank + +Jerasure - A C/C++ Library for a Variety of Reed-Solomon and RAID-6 Erasure Coding Techniques + +Revision 1.2A +May 24, 2011 + +James S. Plank +Department of Electrical Engineering and Computer Science +University of Tennessee +Knoxville, TN 37996 +plank@cs.utk.edu + +Copyright (c) 2011, James S. Plank +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + - Neither the name of the University of Tennessee nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + */ + +#define _WIN32_WINNT 0x0600 // Windows Vista or later + +#include +#include + +#include +#include // 組み込み関数(intrinsic)を使用する場合インクルード + +#include "gf16.h" +#include "gf_jit.h" // ParPar の JIT コード用 + +extern unsigned int cpu_flag; // declared in common2.h + +#ifndef _WIN64 // 32-bit 版なら +#pragma warning(disable:4731) // inhibit VC's "ebp modified" warning +#pragma warning(disable:4799) // inhibit VC's "missing emms" warning +#endif + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// CPU によって使う関数を変更する際の仮宣言 + +//#define NO_SIMD // SIMD を使わない場合 +//#define NO_ALTMAP // SSSE3 や JIT(SSE2) の並び替えを使わない場合 (CLMULや32バイト単位は有効) + +int sse_unit; + +void galois_align16_multiply(unsigned char *r1, unsigned char *r2, unsigned int len, int factor); +void galois_align32_multiply(unsigned char *r1, unsigned char *r2, unsigned int len, int factor); +void galois_align32avx_multiply(unsigned char *r1, unsigned char *r2, unsigned int len, int factor); +void galois_align256_multiply(unsigned char *r1, unsigned char *r2, unsigned int len, int factor); + +void galois_altmap_none(unsigned char *data, unsigned int bsize); + +// AVX2 と SSSE3 の ALTMAP は 32バイト単位で行う +void galois_altmap32_change(unsigned char *data, unsigned int bsize); +void galois_altmap32_return(unsigned char *data, unsigned int bsize); +void checksum16_altmap32(unsigned char *data, unsigned char *hash, int byte_size); +void checksum16_return32(unsigned char *data, unsigned char *hash, int byte_size); + +// JIT(SSE2) は 256バイト単位で計算する +void galois_altmap256_change(unsigned char *data, unsigned int bsize); +void galois_altmap256_return(unsigned char *data, unsigned int bsize); +void checksum16_altmap256(unsigned char *data, unsigned char *hash, int byte_size); +void checksum16_return256(unsigned char *data, unsigned char *hash, int byte_size); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define NW 65536 +#define NWM1 65535 +#define PRIM_POLY 0x1100B + +// なぜかテーブルは 2バイト整数を使った方が速い +static unsigned short *galois_log_table = NULL; +static unsigned short *galois_exp_table; + +int galois_create_table(void) +{ + unsigned int j, b; + + if (galois_log_table != NULL) return 0; + galois_log_table = _aligned_malloc(sizeof(unsigned short) * NW * 2, 64); + if (galois_log_table == NULL) return -1; + galois_exp_table = galois_log_table + NW; // 要素数は 65536個 + + b = 1; + for (j = 0; j < NWM1; j++){ + galois_log_table[b] = (unsigned short)j; + galois_exp_table[j] = (unsigned short)b; + b = b << 1; + if (b & NW) b ^= PRIM_POLY; + } + galois_exp_table[NWM1] = galois_exp_table[0]; // copy for reduction (? mod NWM1) + + // CPU によって使う関数を変更する + sse_unit = 16; // 16, 32, 64, 128 のどれでもいい (32のSSSE3は少し速い、GPUが識別するのに注意) + galois_align_multiply = galois_align16_multiply; + galois_altmap_change = galois_altmap_none; + galois_altmap_return = galois_altmap_none; + checksum16_altmap = checksum16; + checksum16_return = checksum16; +#ifndef NO_SIMD +#ifndef NO_ALTMAP + if (cpu_flag & 16){ // AVX2 対応なら + //printf("\nUse AVX2 & ALTMAP\n"); + sse_unit = 32; // 32, 64, 128 のどれでもいい + galois_align_multiply = galois_align32avx_multiply; + galois_altmap_change = galois_altmap32_change; + galois_altmap_return = galois_altmap32_return; + checksum16_altmap = checksum16_altmap32; + checksum16_return = checksum16_return32; + } else if (cpu_flag & 1){ // SSSE3 対応なら + if ((cpu_flag & 256) == 0){ // SSSE3 & ALTMAP を使う + //printf("\nUse SSSE3 & ALTMAP\n"); + sse_unit = 32; // 32, 64, 128 のどれでもいい + galois_align_multiply = galois_align32_multiply; + galois_altmap_change = galois_altmap32_change; + galois_altmap_return = galois_altmap32_return; + checksum16_altmap = checksum16_altmap32; + checksum16_return = checksum16_return32; + } + } else { // SSSE3 が利用できない場合 + if ((cpu_flag & 128) && (jit_alloc() == 0)){ // JIT(SSE2) を使う + //printf("\nUse JIT(SSE2) & ALTMAP\n"); + sse_unit = 256; + galois_align_multiply = galois_align256_multiply; + galois_altmap_change = galois_altmap256_change; + galois_altmap_return = galois_altmap256_return; + checksum16_altmap = checksum16_altmap256; + checksum16_return = checksum16_return256; + } + } +#endif +#endif + + return 0; +} + +unsigned short galois_multiply(int x, int y) +{ + int sum; + + if ((x == 0) || (y == 0)) return 0; + + sum = galois_log_table[x] + galois_log_table[y]; // result is from 2 to NWM1 * 2 + //if (sum >= NWM1) sum -= NWM1; + sum = (sum >> 16) + (sum & NWM1); // result is from 0 to NWM1 + return galois_exp_table[sum]; +} + +// multiply when "y" is a fixed value +unsigned short galois_multiply_fix(int x, int log_y) +{ + int sum; + + if (x == 0) return 0; + + sum = galois_log_table[x] + log_y; // result is from 2 to NWM1 * 2 + sum = (sum >> 16) + (sum & NWM1); // result is from 0 to NWM1 + return galois_exp_table[sum]; +} + +unsigned short galois_divide(int x, int y) +{ + int sum; + + if (y == 0) return NWM1; // 除算エラー + if (x == 0) return 0; + + sum = galois_log_table[x] - galois_log_table[y]; + if (sum < 0) sum += NWM1; + return galois_exp_table[sum]; +} + +// ガロア体上での乗数計算、x の y 乗 +unsigned short galois_power(int x, int y) +{ + unsigned int sum; + + if (x == 0) return 0; // 0**y = 0 + if (y == 0) return 1; // x**0 = 1 + if (y == 1) return (unsigned short)x; // x**1 = x + + sum = (unsigned int)(galois_log_table[x]) * (unsigned int)y; // result is from 1 to NWM1 * NWM1 + //sum = sum % NWM1; + sum = (sum >> 16) + (sum & NWM1); // result is from 1 to NWM1 * 2 + sum = (sum >> 16) + (sum & NWM1); // result is from 0 to NWM1 + return galois_exp_table[sum]; +} + +// ガロア体上での逆数、1 / x +unsigned short galois_reciprocal(int x) +{ + if (x == 0) return NWM1; // 除算エラー + return galois_exp_table[NWM1 - (int)(galois_log_table[x])]; +} + +void galois_free_table(void) // テーブルを解放するために追加 +{ + if (galois_log_table != NULL){ + _aligned_free(galois_log_table); + galois_log_table = NULL; +#ifndef _WIN64 // 32-bit 版ならインライン・アセンブラを使う + if (((cpu_flag & 1) == 0) && ((cpu_flag & 128) == 0)) // SSSE3 を使わない場合、MMX の終了処理 + _mm_empty(); +#endif + // SSSE3 を使わない場合で、JIT(SSE2) を使った場合の終了処理 + if (((cpu_flag & 1) == 0) && ((cpu_flag & 128) != 0)) + jit_free(); + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// MMX functions are based on code by Paul Houle (paulhoule.com) March 22, 2008 + +#ifndef _WIN64 // 32-bit 版ならインライン・アセンブラを使う + +// Processes block of data a multiple of 8 bytes long using SIMD (mmx) opcodes. +// The amount of data to process (bsize) must be a non-zero multiple of 8. +// Paul's original code was modified to calculate each 8-bytes and removed last shift by Yutaka Sawada +static void DoBlock8(unsigned char *input, unsigned char *output, unsigned int bsize, unsigned int *pMtab) +{ + __asm { + push ebp + mov ebx,bsize ;bytes to process (multiple of 8) + mov esi,input ;source + mov edi,output ;destination + mov ebp,pMtab ;combined multiplication table + + mov eax,[esi] ;load 1st 8 source bytes + movd mm4,[esi+4] + + sub ebx,8 ;reduce last 8-bytes from loop + add esi,ebx ;point to end of input/output + add edi,ebx + neg ebx ;convert byte size to count-up + +lp8: + movzx edx,al + movzx ecx,ah + shr eax,16 + movd mm0,[ebp+edx*4] ;order is [_][_][_][0] + movd mm1,[ebp+400h+ecx*4] + movzx edx,al + movzx ecx,ah + movd eax,mm4 + movq mm4,[esi+ebx+8] ;read-ahead next 8 source bytes + movd mm2,[ebp+edx*4] ;order is [_][_][_][1] + movzx edx,al + movq mm5,[edi+ebx] + movd mm3,[ebp+400h+ecx*4] + movzx ecx,ah + shr eax,16 + punpcklwd mm0,[ebp+edx*4] ;order is [_][_][2][0] + movzx edx,al + punpcklwd mm1,[ebp+400h+ecx*4] + movzx ecx,ah + punpcklwd mm2,[ebp+edx*4] ;order is [_][_][3][1] + pxor mm1,mm0 + punpcklwd mm3,[ebp+400h+ecx*4] + pxor mm3,mm2 + movd eax,mm4 ;prepare src bytes 3-0 for next loop + punpcklwd mm1,mm3 ;order is [3][2][1][0] + psrlq mm4,32 ;align src bytes 7-4 for next loop + pxor mm1,mm5 + movq [edi+ebx],mm1 + add ebx,8 + jnz lp8 + + ;no need to pre-read in last 8-bytes + movzx edx,al + movzx ecx,ah + shr eax,16 + movd mm0,[ebp+edx*4] ;order is [_][_][_][0] + movd mm1,[ebp+400h+ecx*4] + movzx edx,al + movzx ecx,ah + movd eax,mm4 + movd mm2,[ebp+edx*4] ;order is [_][_][_][1] + movzx edx,al + movq mm5,[edi+ebx] + movd mm3,[ebp+400h+ecx*4] + movzx ecx,ah + shr eax,16 + punpcklwd mm0,[ebp+edx*4] ;order is [_][_][2][0] + movzx edx,al + punpcklwd mm1,[ebp+400h+ecx*4] + movzx ecx,ah + punpcklwd mm2,[ebp+edx*4] ;order is [_][_][3][1] + pxor mm1,mm0 + punpcklwd mm3,[ebp+400h+ecx*4] + pxor mm3,mm2 + punpcklwd mm1,mm3 ;order is [3][2][1][0] + pxor mm1,mm5 + movq [edi+ebx],mm1 + + pop ebp + } +} + +#endif + +// calculate multiplication tables instantly +static void create_two_table(unsigned int *mtab, int factor){ + int shift_table[8], i, j, sum; + + // factor * 2の乗数を計算する + shift_table[0] = factor; // factor * 1 + for (i = 1; i < 8; i++){ +// if (factor & 0x8000){ +// factor <<= 1; +// factor ^= 0x1100B; +// } else { +// factor <<= 1; +// } + factor = (factor << 1) ^ (((factor << 16) >> 31) & 0x1100B); + shift_table[i] = factor; // factor * (2**i) + } + + for (j = 0; j < 2; j++){ + for (i = 0; i < 256; i += 32){ +/* + sum = 0; + if (i & 32) + sum = shift_table[5]; + if (i & 64) + sum ^= shift_table[6]; + if (i & 128) + sum ^= shift_table[7]; +*/ + sum = shift_table[5] & ((i << 26) >> 31); + sum ^= shift_table[6] & ((i << 25) >> 31); + sum ^= shift_table[7] & ((i << 24) >> 31); + + mtab[i ] = sum; + mtab[i + 1] = sum ^ shift_table[0]; + mtab[i + 2] = sum ^ shift_table[1]; + mtab[i + 3] = sum ^ shift_table[1] ^ shift_table[0]; + mtab[i + 4] = sum ^ shift_table[2]; + mtab[i + 5] = sum ^ shift_table[2] ^ shift_table[0]; + mtab[i + 6] = sum ^ shift_table[2] ^ shift_table[1]; + mtab[i + 7] = sum ^ shift_table[2] ^ shift_table[1] ^ shift_table[0]; + mtab[i + 8] = sum ^ shift_table[3]; + mtab[i + 9] = sum ^ shift_table[3] ^ shift_table[0]; + mtab[i + 10] = sum ^ shift_table[3] ^ shift_table[1]; + mtab[i + 11] = sum ^ shift_table[3] ^ shift_table[1] ^ shift_table[0]; + mtab[i + 12] = sum ^ shift_table[3] ^ shift_table[2]; + mtab[i + 13] = sum ^ shift_table[3] ^ shift_table[2] ^ shift_table[0]; + mtab[i + 14] = sum ^ shift_table[3] ^ shift_table[2] ^ shift_table[1]; + mtab[i + 15] = sum ^ shift_table[3] ^ shift_table[2] ^ shift_table[1] ^ shift_table[0]; + sum ^= shift_table[4]; + mtab[i + 16] = sum; + mtab[i + 17] = sum ^ shift_table[0]; + mtab[i + 18] = sum ^ shift_table[1]; + mtab[i + 19] = sum ^ shift_table[1] ^ shift_table[0]; + mtab[i + 20] = sum ^ shift_table[2]; + mtab[i + 21] = sum ^ shift_table[2] ^ shift_table[0]; + mtab[i + 22] = sum ^ shift_table[2] ^ shift_table[1]; + mtab[i + 23] = sum ^ shift_table[2] ^ shift_table[1] ^ shift_table[0]; + mtab[i + 24] = sum ^ shift_table[3]; + mtab[i + 25] = sum ^ shift_table[3] ^ shift_table[0]; + mtab[i + 26] = sum ^ shift_table[3] ^ shift_table[1]; + mtab[i + 27] = sum ^ shift_table[3] ^ shift_table[1] ^ shift_table[0]; + mtab[i + 28] = sum ^ shift_table[3] ^ shift_table[2]; + mtab[i + 29] = sum ^ shift_table[3] ^ shift_table[2] ^ shift_table[0]; + mtab[i + 30] = sum ^ shift_table[3] ^ shift_table[2] ^ shift_table[1]; + mtab[i + 31] = sum ^ shift_table[3] ^ shift_table[2] ^ shift_table[1] ^ shift_table[0]; + } + + for (i = 0; i < 8; i++){ + factor = (factor << 1) ^ (((factor << 16) >> 31) & 0x1100B); + shift_table[i] = factor; // factor * (2**i) + } + mtab += 256; + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// The method of using SSSE3 is based on Plank's papar; +// "Screaming Fast Galois Field Arithmetic Using Intel SIMD Instructions". + +/* +static void create_eight_table_c(unsigned char *mtab, int factor) +{ + int shift_table[16], i, sum; + + // factor * 2の乗数を計算する + shift_table[0] = factor; // factor * 1 + for (i = 1; i < 16; i++){ +// if (factor & 0x8000){ +// factor <<= 1; +// factor ^= 0x1100B; +// } else { +// factor <<= 1; +// } + factor = (factor << 1) ^ (((factor << 16) >> 31) & 0x1100B); + shift_table[i] = factor; // factor * (2**i) + } + + for (i = 0; i < 16; i += 4){ // 4-bit ごとに計算する + mtab[i * 8 ] = 0; + mtab[i * 8 + 16] = 0; + sum = shift_table[i]; + mtab[i * 8 + 1] = (unsigned char)sum; // lower 8-bit + mtab[i * 8 + 17] = (unsigned char)(sum >> 8); // higher 8-bit + sum = shift_table[i + 1]; + mtab[i * 8 + 2] = (unsigned char)sum; + mtab[i * 8 + 18] = (unsigned char)(sum >> 8); + sum = sum ^ shift_table[i]; + mtab[i * 8 + 3] = (unsigned char)sum; + mtab[i * 8 + 19] = (unsigned char)(sum >> 8); + sum = shift_table[i + 2]; + mtab[i * 8 + 4] = (unsigned char)sum; + mtab[i * 8 + 20] = (unsigned char)(sum >> 8); + sum = sum ^ shift_table[i]; + mtab[i * 8 + 5] = (unsigned char)sum; + mtab[i * 8 + 21] = (unsigned char)(sum >> 8); + sum = shift_table[i + 2] ^ shift_table[i + 1]; + mtab[i * 8 + 6] = (unsigned char)sum; + mtab[i * 8 + 22] = (unsigned char)(sum >> 8); + sum = sum ^ shift_table[i]; + mtab[i * 8 + 7] = (unsigned char)sum; + mtab[i * 8 + 23] = (unsigned char)(sum >> 8); + sum = shift_table[i + 3]; + mtab[i * 8 + 8] = (unsigned char)sum; + mtab[i * 8 + 24] = (unsigned char)(sum >> 8); + sum = sum ^ shift_table[i]; + mtab[i * 8 + 9] = (unsigned char)sum; + mtab[i * 8 + 25] = (unsigned char)(sum >> 8); + sum = shift_table[i + 3] ^ shift_table[i + 1]; + mtab[i * 8 + 10] = (unsigned char)sum; + mtab[i * 8 + 26] = (unsigned char)(sum >> 8); + sum = sum ^ shift_table[i]; + mtab[i * 8 + 11] = (unsigned char)sum; + mtab[i * 8 + 27] = (unsigned char)(sum >> 8); + sum = shift_table[i + 3] ^ shift_table[i + 2]; + mtab[i * 8 + 12] = (unsigned char)sum; + mtab[i * 8 + 28] = (unsigned char)(sum >> 8); + sum = sum ^ shift_table[i]; + mtab[i * 8 + 13] = (unsigned char)sum; + mtab[i * 8 + 29] = (unsigned char)(sum >> 8); + sum = shift_table[i + 3] ^ shift_table[i + 2] ^ shift_table[i + 1]; + mtab[i * 8 + 14] = (unsigned char)sum; + mtab[i * 8 + 30] = (unsigned char)(sum >> 8); + sum = sum ^ shift_table[i]; + mtab[i * 8 + 15] = (unsigned char)sum; + mtab[i * 8 + 31] = (unsigned char)(sum >> 8); + } +} +*/ + +#ifndef _WIN64 // 32-bit 版ならインライン・アセンブラを使う + +// tables for split four combined multiplication +static void create_eight_table(unsigned char *mtab, int factor) +{ + __asm { // This implementation requires SSE2 + mov eax, factor + mov edx, mtab + mov ecx, -128 + + ; create mask for 8-bit + pcmpeqw xmm7, xmm7 ; 0xFFFF *8 + psrlw xmm7, 8 ; 0x00FF *8 + +lp32: + ; factor * 1, *2, *4, *8 + movd xmm0, eax ; [_][_][_][_][_][_][_][1] + pxor xmm1, xmm1 + + movsx ebx, ax + sar ebx, 31 + shl eax, 1 + and ebx, 0x1100B + xor eax, ebx + pinsrw xmm1, eax, 1 ; [_][_][_][_][_][_][2][_] + pxor xmm2, xmm2 + + movsx ebx, ax + sar ebx, 31 + shl eax, 1 + and ebx, 0x1100B + xor eax, ebx + pinsrw xmm2, eax, 4 ; [_][_][_][4][_][_][_][_] + punpcklwd xmm1, xmm1 ; [_][_][_][_][2][2][_][_] + + movsx ebx, ax + sar ebx, 31 + shl eax, 1 + and ebx, 0x1100B + xor eax, ebx + movd xmm3, eax ; [_][_][_][_][_][_][_][8] + + pshuflw xmm0, xmm0, 17 ; [_][_][_][_][1][_][1][_] + punpcklwd xmm3, xmm3 ; [_][_][_][_][_][_][8][8] + pxor xmm0, xmm1 ; [_][_][_][_][3][2][1][_] + pshufhw xmm2, xmm2, 0 ; [4][4][4][4][_][_][_][_] + punpcklqdq xmm0, xmm0 ; [3][2][1][_][3][2][1][_] + pshufd xmm3, xmm3, 0 ; [8][8][8][8][8][8][8][8] + pxor xmm2, xmm0 ; [7][6][5][4][3][2][1][_] + pxor xmm3, xmm2 ; [15][14][13][12][11][10][9][8] + + movdqa xmm0, xmm2 + movdqa xmm1, xmm3 + pand xmm0, xmm7 + pand xmm1, xmm7 + packuswb xmm0, xmm1 ; lower 8-bit * 16 + psrlw xmm2, 8 + psrlw xmm3, 8 + packuswb xmm2, xmm3 ; higher 8-bit * 16 + + movdqa [edx+128+ecx], xmm0 + movdqa [edx+144+ecx], xmm2 + + ; for next loop + movsx ebx, ax ; move with sign of word + sar ebx, 31 + shl eax, 1 + and ebx, 0x1100B + xor eax, ebx + + add ecx, 32 + jnz lp32 + } +} + +// VC2008 は SSSE3 をインライン・アセンブラで使える +// Address (input) does not need be 16-byte aligned +static void gf16_ssse3_block16u(unsigned char *input, unsigned char *output, unsigned int bsize, unsigned char *table) +{ + __asm { + mov ecx, input ; source + mov edx, output ; destination + mov eax, bsize ; bytes to process (multiple of 16) + mov ebx, table ; multiplication table + + ; create mask for 8 entries + pcmpeqw xmm7, xmm7 ; 0xFFFF *8 + psrlw xmm7, 12 ; 0x000F *8 + + add ecx, eax ; point to end of input/output + add edx, eax + neg eax ; convert byte size to count-up + +lp16: + movdqu xmm0, [ecx+eax] ; read source 16-bytes + movdqa xmm2, [edx+eax] + + movdqa xmm3, [ebx] ; low table + movdqa xmm4, [ebx+16] ; high table + movdqa xmm1, xmm0 ; copy source + psrlw xmm0, 4 ; prepare next 4-bit + pand xmm1, xmm7 ; src & 0x000F + pshufb xmm3, xmm1 ; table look-up + psllw xmm1, 8 ; shift 8-bit for higher table + pshufb xmm4, xmm1 + movdqa xmm5, [ebx+32] ; low table + movdqa xmm6, [ebx+48] ; high table + pxor xmm3, xmm4 ; combine high and low + pxor xmm2, xmm3 + + movdqa xmm1, xmm0 ; copy source + psrlw xmm0, 4 ; prepare next 4-bit + pand xmm1, xmm7 ; (src >> 4) & 0x000F + pshufb xmm5, xmm1 ; table look-up + psllw xmm1, 8 ; shift 8-bit for higher table + pshufb xmm6, xmm1 + movdqa xmm3, [ebx+64] ; low table + movdqa xmm4, [ebx+80] ; high table + pxor xmm5, xmm6 ; combine high and low + pxor xmm2, xmm5 + + movdqa xmm1, xmm0 ; copy source + psrlw xmm0, 4 ; prepare next 4-bit + pand xmm1, xmm7 ; (src >> 8) & 0x000F + pshufb xmm3, xmm1 ; table look-up + psllw xmm1, 8 ; shift 8-bit for higher table + pshufb xmm4, xmm1 + movdqa xmm5, [ebx+96] ; low table + movdqa xmm6, [ebx+112] ; high table + pxor xmm3, xmm4 ; combine high and low + pxor xmm2, xmm3 + + pshufb xmm5, xmm0 ; table look-up + psllw xmm0, 8 ; shift 8-bit for higher table + pshufb xmm6, xmm0 + pxor xmm5, xmm6 ; combine high and low + pxor xmm2, xmm5 + + movdqa [edx+eax], xmm2 + + add eax, 16 + jnz lp16 + } +} + +// その場で 32バイトごとに並び替えて、計算後に戻す方法(50% faster than 16-byte version) +// Address (input) does not need be 16-byte aligned +static void gf16_ssse3_block32u(unsigned char *input, unsigned char *output, unsigned int bsize, unsigned char *table) +{ + __asm { + mov ecx, input ; source + mov edx, output ; destination + mov eax, bsize ; bytes to process (multiple of 32) + mov ebx, table ; multiplication table + + ; create mask for 16 entries + pcmpeqw xmm7, xmm7 ; 0xFFFF *8 + pcmpeqw xmm6, xmm6 ; 0xFFFF *8 + psrlw xmm7, 12 ; 0x000F *8 + psrlw xmm6, 8 ; 0x00FF *8 + packuswb xmm7, xmm7 ; 0x0F *16 + + add ecx, eax ; point to end of input/output + add edx, eax + neg eax ; convert byte size to count-up + +lp32: + movdqu xmm0, [ecx+eax ] ; read source 32-bytes + movdqu xmm2, [ecx+eax+16] + movdqa xmm1, xmm0 ; copy source + movdqa xmm3, xmm2 + pand xmm0, xmm6 ; erase higher byte + pand xmm2, xmm6 + psrlw xmm1, 8 ; move higher byte to lower + psrlw xmm3, 8 + packuswb xmm0, xmm2 ; select lower byte of each word + packuswb xmm1, xmm3 ; select higher byte of each word + + movdqa xmm4, [ebx] ; low table + movdqa xmm5, [ebx+16] ; high table + movdqa xmm3, xmm0 ; copy source + psrlw xmm0, 4 ; prepare next 4-bit + pand xmm3, xmm7 ; src & 0x0F + pand xmm0, xmm7 ; (src >> 4) & 0x0F + pshufb xmm4, xmm3 ; table look-up + pshufb xmm5, xmm3 + + movdqa xmm2, [ebx+32] ; low table + movdqa xmm3, [ebx+48] ; high table + pshufb xmm2, xmm0 ; table look-up + pshufb xmm3, xmm0 + pxor xmm4, xmm2 ; combine result + pxor xmm5, xmm3 + + movdqa xmm2, [ebx+64] ; low table + movdqa xmm3, [ebx+80] ; high table + movdqa xmm0, xmm1 ; copy source + psrlw xmm1, 4 ; prepare next 4-bit + pand xmm0, xmm7 ; src & 0x0F + pand xmm1, xmm7 ; (src >> 4) & 0x0F + pshufb xmm2, xmm0 ; table look-up + pshufb xmm3, xmm0 + pxor xmm4, xmm2 ; combine result + pxor xmm5, xmm3 + + movdqa xmm2, [ebx+96] ; low table + movdqa xmm3, [ebx+112] ; high table + pshufb xmm2, xmm1 ; table look-up + pshufb xmm3, xmm1 + pxor xmm4, xmm2 ; combine result + pxor xmm5, xmm3 + + movdqa xmm0, [edx+eax] ; read dest 32-bytes + movdqa xmm1, [edx+eax+16] + movdqa xmm3, xmm4 ; copy result + punpcklbw xmm3, xmm5 ; interleave lower and higher bytes + punpckhbw xmm4, xmm5 + pxor xmm0, xmm3 + pxor xmm1, xmm4 + movdqa [edx+eax], xmm0 ; write dest 32-bytes + movdqa [edx+eax+16], xmm1 + + add eax, 32 + jnz lp32 + } +} + +// 先に 32バイトごとに並び替えてあるデータを扱う方法 +static void gf16_ssse3_block32_altmap(unsigned char *input, unsigned char *output, unsigned int bsize, unsigned char *table) +{ + __asm { + mov ecx, input ; source + mov edx, output ; destination + mov eax, bsize ; bytes to process (multiple of 32) + mov ebx, table ; multiplication table + + ; create mask for 16 entries + pcmpeqw xmm7, xmm7 ; 0xFFFF *8 + psrlw xmm7, 12 ; 0x000F *8 + packuswb xmm7, xmm7 ; 0x0F *16 + + add ecx, eax ; point to end of input/output + add edx, eax + neg eax ; convert byte size to count-up + +lp32: + movdqa xmm0, [ecx+eax] ; read source 32-bytes + movdqa xmm1, [ecx+eax+16] + + movdqa xmm4, [ebx] ; low table + movdqa xmm5, [ebx+16] ; high table + movdqa xmm3, xmm0 ; copy source + psrlw xmm0, 4 ; prepare next 4-bit + pand xmm3, xmm7 ; src & 0x0F + pand xmm0, xmm7 ; (src >> 4) & 0x0F + pshufb xmm4, xmm3 ; table look-up + pshufb xmm5, xmm3 + + movdqa xmm2, [ebx+32] ; low table + movdqa xmm3, [ebx+48] ; high table + pshufb xmm2, xmm0 ; table look-up + pshufb xmm3, xmm0 + pxor xmm2, xmm4 ; combine result + pxor xmm3, xmm5 + + movdqa xmm4, [ebx+64] ; low table + movdqa xmm5, [ebx+80] ; high table + movdqa xmm0, xmm1 ; copy source + psrlw xmm0, 4 ; prepare next 4-bit + pand xmm1, xmm7 ; src & 0x0F + pand xmm0, xmm7 ; (src >> 4) & 0x0F + pshufb xmm4, xmm1 ; table look-up + pshufb xmm5, xmm1 + pxor xmm4, xmm2 ; combine result + pxor xmm5, xmm3 + + movdqa xmm2, [ebx+96] ; low table + movdqa xmm3, [ebx+112] ; high table + pshufb xmm2, xmm0 ; table look-up + pshufb xmm3, xmm0 + + movdqa xmm0, [edx+eax] ; read dest 32-bytes + movdqa xmm1, [edx+eax+16] + pxor xmm4, xmm2 ; combine result + pxor xmm5, xmm3 + pxor xmm4, xmm0 + pxor xmm5, xmm1 + movdqa [edx+eax], xmm4 ; write dest 32-bytes + movdqa [edx+eax+16], xmm5 + + add eax, 32 + jnz lp32 + } +} + +#else // 64-bit 版ではインライン・アセンブラを使えない +// (__m128i *) で逐次ポインターをキャスト変換するよりも、 +// 先に __m128i* で定義しておいた方が、連続した領域へのアクセス最適化がうまくいく? + +// tables for split four combined multiplication +static void create_eight_table(unsigned char *mtab, int factor){ + int count = 4; + __m128i *tbl; + __m128i xmm0, xmm1, xmm2, xmm3, xmm7; + + tbl = (__m128i *)mtab; + + // create mask for 8-bit + xmm7 = _mm_setzero_si128(); + xmm7 = _mm_cmpeq_epi16(xmm7, xmm7); // 0xFFFF *8 + xmm7 = _mm_srli_epi16(xmm7, 8); // 0x00FF *8 + + while (1){ + xmm0 = _mm_cvtsi32_si128(factor); // [_][_][_][_][_][_][_][1] + xmm1 = _mm_setzero_si128(); + + factor = (factor << 1) ^ (((factor << 16) >> 31) & 0x1100B); + xmm1 = _mm_insert_epi16(xmm1, factor, 1); // [_][_][_][_][_][_][2][_] + xmm2 = _mm_setzero_si128(); + + factor = (factor << 1) ^ (((factor << 16) >> 31) & 0x1100B); + xmm2 = _mm_insert_epi16(xmm2, factor, 4); // [_][_][_][4][_][_][_][_] + xmm1 = _mm_unpacklo_epi16(xmm1, xmm1); // [_][_][_][_][2][2][_][_] + + factor = (factor << 1) ^ (((factor << 16) >> 31) & 0x1100B); + xmm3 = _mm_cvtsi32_si128(factor); // [_][_][_][_][_][_][_][8] + + xmm0 = _mm_shufflelo_epi16(xmm0, _MM_SHUFFLE(0, 1, 0, 1)); // [_][_][_][_][1][_][1][_] + xmm3 = _mm_unpacklo_epi16(xmm3, xmm3); // [_][_][_][_][_][_][8][8] + xmm0 = _mm_xor_si128(xmm0, xmm1); // [_][_][_][_][3][2][1][_] + xmm2 = _mm_shufflehi_epi16(xmm2, _MM_SHUFFLE(0, 0, 0, 0)); // [4][4][4][4][_][_][_][_] + xmm0 = _mm_unpacklo_epi64(xmm0, xmm0); // [3][2][1][_][3][2][1][_] + xmm3 = _mm_shuffle_epi32(xmm3, _MM_SHUFFLE(0, 0, 0, 0)); // [8][8][8][8][8][8][8][8] + xmm2 = _mm_xor_si128(xmm2, xmm0); // [7][6][5][4][3][2][1][_] + xmm3 = _mm_xor_si128(xmm3, xmm2); // [15][14][13][12][11][10][9][8] + + xmm0 = _mm_load_si128(&xmm2); + xmm1 = _mm_load_si128(&xmm3); + xmm0 = _mm_and_si128(xmm0, xmm7); + xmm1 = _mm_and_si128(xmm1, xmm7); + xmm0 = _mm_packus_epi16(xmm0, xmm1); // lower 8-bit * 16 + xmm2 = _mm_srli_epi16(xmm2, 8); + xmm3 = _mm_srli_epi16(xmm3, 8); + xmm2 = _mm_packus_epi16(xmm2, xmm3); // higher 8-bit * 16 + + _mm_store_si128(tbl , xmm0); + _mm_store_si128(tbl + 1, xmm2); + + count--; + if (count == 0) + break; + + factor = (factor << 1) ^ (((factor << 16) >> 31) & 0x1100B); + tbl += 2; + } +} + +// 16バイトごとに計算する方法、_mm_shuffle_epi8 の利用効率が悪い。 +// Address (input) does not need be 16-byte aligned +static void gf16_ssse3_block16u(unsigned char *input, unsigned char *output, unsigned int bsize, unsigned char *table) +{ + __m128i *src, *dst, *tbl; + __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; + + src = (__m128i *)input; + dst = (__m128i *)output; + tbl = (__m128i *)table; + + // create mask for 8 entries + xmm7 = _mm_setzero_si128(); + xmm7 = _mm_cmpeq_epi16(xmm7, xmm7); // 0xFFFF *8 + xmm7 = _mm_srli_epi16(xmm7, 12); // 0x000F *8 + + while (bsize != 0){ + xmm0 = _mm_loadu_si128(src); // read source 16-bytes + xmm2 = _mm_load_si128(dst); + + xmm3 = _mm_load_si128(tbl); // low table + xmm4 = _mm_load_si128(tbl + 1); // high table + xmm1 = _mm_load_si128(&xmm0); // copy source + xmm0 = _mm_srli_epi16(xmm0, 4); // prepare next 4-bit + xmm1 = _mm_and_si128(xmm1, xmm7); // src & 0x000F + xmm3 = _mm_shuffle_epi8(xmm3, xmm1); // table look-up + xmm1 = _mm_slli_epi16(xmm1, 8); // shift 8-bit for higher table + xmm4 = _mm_shuffle_epi8(xmm4, xmm1); + xmm5 = _mm_load_si128(tbl + 2); // low table + xmm6 = _mm_load_si128(tbl + 3); // high table + xmm3 = _mm_xor_si128(xmm3, xmm4); // combine high and low + xmm2 = _mm_xor_si128(xmm2, xmm3); + + xmm1 = _mm_load_si128(&xmm0); // copy source + xmm0 = _mm_srli_epi16(xmm0, 4); // prepare next 4-bit + xmm1 = _mm_and_si128(xmm1, xmm7); // src & 0x000F + xmm5 = _mm_shuffle_epi8(xmm5, xmm1); // table look-up + xmm1 = _mm_slli_epi16(xmm1, 8); // shift 8-bit for higher table + xmm6 = _mm_shuffle_epi8(xmm6, xmm1); + xmm3 = _mm_load_si128(tbl + 4); // low table + xmm4 = _mm_load_si128(tbl + 5); // high table + xmm5 = _mm_xor_si128(xmm5, xmm6); // combine high and low + xmm2 = _mm_xor_si128(xmm2, xmm5); + + xmm1 = _mm_load_si128(&xmm0); // copy source + xmm0 = _mm_srli_epi16(xmm0, 4); // prepare next 4-bit + xmm1 = _mm_and_si128(xmm1, xmm7); // src & 0x000F + xmm3 = _mm_shuffle_epi8(xmm3, xmm1); // table look-up + xmm1 = _mm_slli_epi16(xmm1, 8); // shift 8-bit for higher table + xmm4 = _mm_shuffle_epi8(xmm4, xmm1); + xmm5 = _mm_load_si128(tbl + 6); // low table + xmm6 = _mm_load_si128(tbl + 7); // high table + xmm3 = _mm_xor_si128(xmm3, xmm4); // combine high and low + xmm2 = _mm_xor_si128(xmm2, xmm3); + + xmm5 = _mm_shuffle_epi8(xmm5, xmm0); // table look-up + xmm0 = _mm_slli_epi16(xmm0, 8); // shift 8-bit for higher table + xmm6 = _mm_shuffle_epi8(xmm6, xmm0); + xmm5 = _mm_xor_si128(xmm5, xmm6); // combine high and low + xmm2 = _mm_xor_si128(xmm2, xmm5); + + _mm_store_si128(dst, xmm2); + + src += 1; + dst += 1; + bsize -= 16; + } +} + +// その場で 32バイトごとに並び替えて、計算後に戻す方法(50% faster than 16-byte version) +// なぜか asm を使わない方が速い!? 32-bit と 64-bit の両方で使える +// xmm レジスタを 8個までしか使わない方が 32-bit 版で速いし安定する +// Address (input) does not need be 16-byte aligned +static void gf16_ssse3_block32u(unsigned char *input, unsigned char *output, unsigned int bsize, unsigned char *table) +{ + __m128i *src, *dst; + __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; + __m128i tbl0, tbl1, tbl2, tbl3, tbl4, tbl5, tbl6, tbl7; + + src = (__m128i *)input; + dst = (__m128i *)output; + + // copy tables to local + tbl0 = _mm_load_si128((__m128i *)table); + tbl1 = _mm_load_si128((__m128i *)table + 1); + tbl2 = _mm_load_si128((__m128i *)table + 2); + tbl3 = _mm_load_si128((__m128i *)table + 3); + tbl4 = _mm_load_si128((__m128i *)table + 4); + tbl5 = _mm_load_si128((__m128i *)table + 5); + tbl6 = _mm_load_si128((__m128i *)table + 6); + tbl7 = _mm_load_si128((__m128i *)table + 7); + + // create mask for 16 entries + xmm7 = _mm_setzero_si128(); + xmm7 = _mm_cmpeq_epi16(xmm7, xmm7); // 0xFFFF *8 + xmm6 = _mm_srli_epi16(xmm7, 8); // 0x00FF *8 + xmm7 = _mm_srli_epi16(xmm7, 12); // 0x000F *8 + xmm7 = _mm_packus_epi16(xmm7, xmm7); // 0x0F *16 + + while (bsize != 0){ + xmm1 = _mm_loadu_si128(src); // read source 32-bytes + xmm3 = _mm_loadu_si128(src + 1); + xmm0 = _mm_and_si128(xmm1, xmm6); // erase higher byte + xmm2 = _mm_and_si128(xmm3, xmm6); + xmm1 = _mm_srli_epi16(xmm1, 8); // move higher byte to lower + xmm3 = _mm_srli_epi16(xmm3, 8); + xmm0 = _mm_packus_epi16(xmm0, xmm2); // select lower byte of each word + xmm1 = _mm_packus_epi16(xmm1, xmm3); // select higher byte of each word + + xmm4 = _mm_load_si128(&tbl0); // load tables + xmm5 = _mm_load_si128(&tbl1); + xmm3 = _mm_and_si128(xmm0, xmm7); // src & 0x0F + xmm0 = _mm_and_si128(_mm_srli_epi16(xmm0, 4), xmm7); // (src >> 4) & 0x0F + xmm4 = _mm_shuffle_epi8(xmm4, xmm3); // table look-up + xmm5 = _mm_shuffle_epi8(xmm5, xmm3); + + xmm2 = _mm_load_si128(&tbl2); // load tables + xmm3 = _mm_load_si128(&tbl3); + xmm2 = _mm_shuffle_epi8(xmm2, xmm0); // table look-up + xmm3 = _mm_shuffle_epi8(xmm3, xmm0); + xmm4 = _mm_xor_si128(xmm4, xmm2); // combine result + xmm5 = _mm_xor_si128(xmm5, xmm3); + + xmm2 = _mm_load_si128(&tbl4); // load tables + xmm3 = _mm_load_si128(&tbl5); + xmm0 = _mm_and_si128(xmm1, xmm7); // src & 0x0F + xmm1 = _mm_and_si128(_mm_srli_epi16(xmm1, 4), xmm7); // (src >> 4) & 0x0F + xmm2 = _mm_shuffle_epi8(xmm2, xmm0); // table look-up + xmm3 = _mm_shuffle_epi8(xmm3, xmm0); + xmm4 = _mm_xor_si128(xmm4, xmm2); // combine result + xmm5 = _mm_xor_si128(xmm5, xmm3); + + xmm2 = _mm_load_si128(&tbl6); // load tables + xmm3 = _mm_load_si128(&tbl7); + xmm2 = _mm_shuffle_epi8(xmm2, xmm1); // table look-up + xmm3 = _mm_shuffle_epi8(xmm3, xmm1); + xmm4 = _mm_xor_si128(xmm4, xmm2); // combine result + xmm5 = _mm_xor_si128(xmm5, xmm3); + + xmm0 = _mm_load_si128(dst); // read dest 32-bytes + xmm1 = _mm_load_si128(dst + 1); + xmm3 = _mm_unpacklo_epi8(xmm4, xmm5); // interleave lower and higher bytes + xmm4 = _mm_unpackhi_epi8(xmm4, xmm5); + xmm0 = _mm_xor_si128(xmm0, xmm3); + xmm1 = _mm_xor_si128(xmm1, xmm4); + _mm_store_si128(dst, xmm0); // write dest 32-bytes + _mm_store_si128(dst + 1, xmm1); + + src += 2; + dst += 2; + bsize -= 32; + } +} + +// xmm レジスタにテーブルを読み込む方が 64-bit 版で微妙に速い +static void gf16_ssse3_block32_altmap(unsigned char *input, unsigned char *output, unsigned int bsize, unsigned char *table) +{ + __m128i *src, *dst; + __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm7; + __m128i tbl0, tbl1, tbl2, tbl3, tbl4, tbl5, tbl6, tbl7; + + src = (__m128i *)input; + dst = (__m128i *)output; + + // copy tables to local + tbl0 = _mm_load_si128((__m128i *)table); + tbl1 = _mm_load_si128((__m128i *)table + 1); + tbl2 = _mm_load_si128((__m128i *)table + 2); + tbl3 = _mm_load_si128((__m128i *)table + 3); + tbl4 = _mm_load_si128((__m128i *)table + 4); + tbl5 = _mm_load_si128((__m128i *)table + 5); + tbl6 = _mm_load_si128((__m128i *)table + 6); + tbl7 = _mm_load_si128((__m128i *)table + 7); + + // create mask for 16 entries + xmm7 = _mm_setzero_si128(); + xmm7 = _mm_cmpeq_epi16(xmm7, xmm7); // 0xFFFF *8 + xmm7 = _mm_srli_epi16(xmm7, 12); // 0x000F *8 + xmm7 = _mm_packus_epi16(xmm7, xmm7); // 0x0F *16 + + while (bsize != 0){ + xmm0 = _mm_load_si128(src); // read source 32-bytes + xmm1 = _mm_load_si128(src + 1); + + xmm3 = _mm_load_si128(&xmm0); // copy source + xmm0 = _mm_srli_epi16(xmm0, 4); // prepare next 4-bit + xmm3 = _mm_and_si128(xmm3, xmm7); // src & 0x0F + xmm0 = _mm_and_si128(xmm0, xmm7); // (src >> 4) & 0x0F + + xmm4 = _mm_load_si128(&tbl0); // load tables + xmm5 = _mm_load_si128(&tbl1); + xmm4 = _mm_shuffle_epi8(xmm4, xmm3); // table look-up + xmm5 = _mm_shuffle_epi8(xmm5, xmm3); + + xmm2 = _mm_load_si128(&tbl2); // load tables + xmm3 = _mm_load_si128(&tbl3); + xmm2 = _mm_shuffle_epi8(xmm2, xmm0); // table look-up + xmm3 = _mm_shuffle_epi8(xmm3, xmm0); + xmm4 = _mm_xor_si128(xmm4, xmm2); // combine result + xmm5 = _mm_xor_si128(xmm5, xmm3); + + xmm0 = _mm_load_si128(&xmm1); // copy source + xmm0 = _mm_srli_epi16(xmm0, 4); // prepare next 4-bit + xmm1 = _mm_and_si128(xmm1, xmm7); // src & 0x0F + xmm0 = _mm_and_si128(xmm0, xmm7); // (src >> 4) & 0x0F + + xmm2 = _mm_load_si128(&tbl4); // load tables + xmm3 = _mm_load_si128(&tbl5); + xmm2 = _mm_shuffle_epi8(xmm2, xmm1); // table look-up + xmm3 = _mm_shuffle_epi8(xmm3, xmm1); + xmm4 = _mm_xor_si128(xmm4, xmm2); // combine result + xmm5 = _mm_xor_si128(xmm5, xmm3); + + xmm2 = _mm_load_si128(&tbl6); // load tables + xmm3 = _mm_load_si128(&tbl7); + xmm2 = _mm_shuffle_epi8(xmm2, xmm0); // table look-up + xmm3 = _mm_shuffle_epi8(xmm3, xmm0); + + xmm0 = _mm_load_si128(dst); // read dest 32-bytes + xmm1 = _mm_load_si128(dst + 1); + xmm4 = _mm_xor_si128(xmm4, xmm2); // combine result + xmm5 = _mm_xor_si128(xmm5, xmm3); + xmm4 = _mm_xor_si128(xmm4, xmm0); + xmm5 = _mm_xor_si128(xmm5, xmm1); + _mm_store_si128(dst, xmm4); // write dest 32-bytes + _mm_store_si128(dst + 1, xmm5); + + src += 2; + dst += 2; + bsize -= 32; + } +} + +/* +static void gf16_ssse3_block32_altmap(unsigned char *input, unsigned char *output, unsigned int bsize, unsigned char *table) +{ + __m128i *src, *dst, *tbl; + __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm7; + + src = (__m128i *)input; + dst = (__m128i *)output; + tbl = (__m128i *)table; + + // create mask for 16 entries + xmm7 = _mm_setzero_si128(); + xmm7 = _mm_cmpeq_epi16(xmm7, xmm7); // 0xFFFF *8 + xmm7 = _mm_srli_epi16(xmm7, 12); // 0x000F *8 + xmm7 = _mm_packus_epi16(xmm7, xmm7); // 0x0F *16 + + while (bsize != 0){ + xmm0 = _mm_load_si128(src); // read source 32-bytes + xmm1 = _mm_load_si128(src + 1); + + xmm3 = _mm_load_si128(&xmm0); // copy source + xmm0 = _mm_srli_epi16(xmm0, 4); // prepare next 4-bit + xmm3 = _mm_and_si128(xmm3, xmm7); // src & 0x0F + xmm0 = _mm_and_si128(xmm0, xmm7); // (src >> 4) & 0x0F + + xmm4 = _mm_load_si128(tbl); // load tables + xmm5 = _mm_load_si128(tbl + 1); + xmm4 = _mm_shuffle_epi8(xmm4, xmm3); // table look-up + xmm5 = _mm_shuffle_epi8(xmm5, xmm3); + + xmm2 = _mm_load_si128(tbl + 2); // load tables + xmm3 = _mm_load_si128(tbl + 3); + xmm2 = _mm_shuffle_epi8(xmm2, xmm0); // table look-up + xmm3 = _mm_shuffle_epi8(xmm3, xmm0); + xmm4 = _mm_xor_si128(xmm4, xmm2); // combine result + xmm5 = _mm_xor_si128(xmm5, xmm3); + + xmm0 = _mm_load_si128(&xmm1); // copy source + xmm0 = _mm_srli_epi16(xmm0, 4); // prepare next 4-bit + xmm1 = _mm_and_si128(xmm1, xmm7); // src & 0x0F + xmm0 = _mm_and_si128(xmm0, xmm7); // (src >> 4) & 0x0F + + xmm2 = _mm_load_si128(tbl + 4); // load tables + xmm3 = _mm_load_si128(tbl + 5); + xmm2 = _mm_shuffle_epi8(xmm2, xmm1); // table look-up + xmm3 = _mm_shuffle_epi8(xmm3, xmm1); + xmm4 = _mm_xor_si128(xmm4, xmm2); // combine result + xmm5 = _mm_xor_si128(xmm5, xmm3); + + xmm2 = _mm_load_si128(tbl + 6); // load tables + xmm3 = _mm_load_si128(tbl + 7); + xmm2 = _mm_shuffle_epi8(xmm2, xmm0); // table look-up + xmm3 = _mm_shuffle_epi8(xmm3, xmm0); + + xmm0 = _mm_load_si128(dst); // read dest 32-bytes + xmm1 = _mm_load_si128(dst + 1); + xmm4 = _mm_xor_si128(xmm4, xmm2); // combine result + xmm5 = _mm_xor_si128(xmm5, xmm3); + xmm4 = _mm_xor_si128(xmm4, xmm0); + xmm5 = _mm_xor_si128(xmm5, xmm1); + _mm_store_si128(dst, xmm4); // write dest 32-bytes + _mm_store_si128(dst + 1, xmm5); + + src += 2; + dst += 2; + bsize -= 32; + } +} +*/ + +#endif + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// AVX2 命令を使うには Windows 7 以降じゃないといけない + +// _mm256_permute2x128_si256 の control の意味は以下を参照 +// http://www.felixcloutier.com/x86/VPERM2I128.html + +// テーブルを並び替えて使えば、ループ内の並び替え回数を一回に減らせる +static void gf16_avx2_block32(unsigned char *input, unsigned char *output, unsigned int bsize, unsigned char *table) +{ + __m256i tbl0, tbl1, tbl2, tbl3, mask, src0, src1, tmp0, tmp1, tmp2, tmp3; + + // copy tables to local + tmp0 = _mm256_load_si256((__m256i *)table); // tbl0[low0][high0] <- 0x0f[lo][lo] + tmp1 = _mm256_load_si256((__m256i *)table + 1); // tbl1[low1][high1] <- 0xf0[lo][lo] + tmp2 = _mm256_load_si256((__m256i *)table + 2); // tbl2[low2][high2] <- 0x0f[hi][hi] + tmp3 = _mm256_load_si256((__m256i *)table + 3); // tbl3[low3][high3] <- 0xf0[hi][hi] + + // re-arrange table order + tbl0 = _mm256_permute2x128_si256(tmp0, tmp2, 0x30); // tblA[low0][high2] <- 0x0f[lo][hi] + tbl1 = _mm256_permute2x128_si256(tmp1, tmp3, 0x30); // tblB[low1][high3] <- 0xf0[lo][hi] + tbl2 = _mm256_permute2x128_si256(tmp2, tmp0, 0x03); // tblC[high0][low2] <- 0x0f[lo][hi] + tbl3 = _mm256_permute2x128_si256(tmp3, tmp1, 0x03); // tblD[high1][low3] <- 0xf0[lo][hi] + + // create mask for 32 entries + mask = _mm256_cmpeq_epi16(tmp0, tmp0); // 0xFFFF *16 + mask = _mm256_srli_epi16(mask, 12); // 0x000F *16 + mask = _mm256_packus_epi16(mask, mask); // 0x0F *32 + + while (bsize != 0){ + src0 = _mm256_load_si256((__m256i *)input); // read source 32-bytes + src1 = _mm256_srli_epi16(src0, 4); // prepare next 4-bit + src0 = _mm256_and_si256(src0, mask); // src & 0x0F + src1 = _mm256_and_si256(src1, mask); // (src >> 4) & 0x0F + + tmp0 = _mm256_shuffle_epi8(tbl0, src0); // table look-up + tmp1 = _mm256_shuffle_epi8(tbl1, src1); + tmp2 = _mm256_shuffle_epi8(tbl2, src0); + tmp3 = _mm256_shuffle_epi8(tbl3, src1); + + tmp0 = _mm256_xor_si256(tmp0, tmp1); // combine result + tmp2 = _mm256_xor_si256(tmp2, tmp3); + tmp2 = _mm256_permute2x128_si256(tmp2, tmp2, 0x01); // exchange low & high 128-bit + + src1 = _mm256_load_si256((__m256i *)output); // read dest 32-bytes + src1 = _mm256_xor_si256(src1, tmp0); + src1 = _mm256_xor_si256(src1, tmp2); + _mm256_store_si256((__m256i *)output, src1); // write dest 32-bytes + + input += 32; + output += 32; + bsize -= 32; + } + + // AVX-SSE 切り替えの回避 + _mm256_zeroupper(); +} + +/* +// テーブルを並び替えて使えば、ループ内の並び替え回数を減らせる +static void gf16_avx2_block32(unsigned char *input, unsigned char *output, unsigned int bsize, unsigned char *table) +{ + __m256i tbl0, tbl1, tbl2, tbl3, mask, dest, src0, src1, src2, src3; + + // copy tables to local + src0 = _mm256_load_si256((__m256i *)table); // tbl0[low0][high0] <- 0x0f[lo][lo] + src1 = _mm256_load_si256((__m256i *)table + 1); // tbl1[low1][high1] <- 0xf0[lo][lo] + src2 = _mm256_load_si256((__m256i *)table + 2); // tbl2[low2][high2] <- 0x0f[hi][hi] + src3 = _mm256_load_si256((__m256i *)table + 3); // tbl3[low3][high3] <- 0xf0[hi][hi] + + // re-arrange table order + tbl0 = _mm256_permute2x128_si256(src0, src2, 0x30); // tblA[low0][high2] <- 0x0f[lo][hi] + tbl1 = _mm256_permute2x128_si256(src1, src3, 0x30); // tblB[low1][high3] <- 0xf0[lo][hi] + tbl2 = _mm256_permute2x128_si256(src2, src0, 0x30); // tblC[low2][high0] <- 0x0f[hi][lo] + tbl3 = _mm256_permute2x128_si256(src3, src1, 0x30); // tblD[low3][high1] <- 0xf0[hi][lo] + + // create mask for 32 entries + mask = _mm256_set1_epi8(0x0F); // 0x0F *32 + + while (bsize != 0){ + src0 = _mm256_load_si256((__m256i *)input); // read source 32-bytes + dest = _mm256_load_si256((__m256i *)output); // read dest 32-bytes + + src1 = _mm256_srli_epi16(src0, 4); // prepare next 4-bit + src0 = _mm256_and_si256(src0, mask); // src & 0x0F + src1 = _mm256_and_si256(src1, mask); // (src >> 4) & 0x0F + + src2 = _mm256_permute2x128_si256(src0, src0, 0x01); // exchange low & high 128-bit of "src & 0x0F" + src3 = _mm256_permute2x128_si256(src1, src1, 0x01); // exchange low & high 128-bit of "(src >> 4) & 0x0F" + + src0 = _mm256_shuffle_epi8(tbl0, src0); // table look-up + src1 = _mm256_shuffle_epi8(tbl1, src1); + src2 = _mm256_shuffle_epi8(tbl2, src2); + src3 = _mm256_shuffle_epi8(tbl3, src3); + + src0 = _mm256_xor_si256(src0, src1); // combine result + src2 = _mm256_xor_si256(src2, src3); + dest = _mm256_xor_si256(dest, src0); + dest = _mm256_xor_si256(dest, src2); + _mm256_store_si256((__m256i *)output, dest); // write dest 32-bytes + + input += 32; + output += 32; + bsize -= 32; + } + + // AVX-SSE 切り替えの回避 + _mm256_zeroupper(); +} +*/ + +/* +// レジスタを大量に使って依存関係をなくせば並列処理できるかも? +static void gf16_avx2_block32(unsigned char *input, unsigned char *output, unsigned int bsize, unsigned char *table) +{ + __m256i tbl0, tbl1, tbl2, tbl3, mask, dest, src0, src1, tmp0, tmp1, tmp2, tmp3; + + // copy tables to local + tbl0 = _mm256_load_si256((__m256i *)table); + tbl1 = _mm256_load_si256((__m256i *)table + 1); + tbl2 = _mm256_load_si256((__m256i *)table + 2); + tbl3 = _mm256_load_si256((__m256i *)table + 3); + + // create mask for 32 entries + mask = _mm256_set1_epi8(0x0F); // 0x0F *32 + + while (bsize != 0){ + src0 = _mm256_load_si256((__m256i *)input); // read source 32-bytes + dest = _mm256_load_si256((__m256i *)output); // read dest 32-bytes + + src1 = _mm256_srli_epi16(src0, 4); // prepare next 4-bit + src0 = _mm256_and_si256(src0, mask); // src & 0x0F + src1 = _mm256_and_si256(src1, mask); // (src >> 4) & 0x0F + + tmp0 = _mm256_permute2x128_si256(src0, src0, 0x00); // copy low 128-bit to high from "src & 0x0F" + tmp1 = _mm256_permute2x128_si256(src1, src1, 0x00); // copy low 128-bit to high from "(src >> 4) & 0x0F" + tmp2 = _mm256_permute2x128_si256(src0, src0, 0x11); // copy high 128-bit to low from "src & 0x0F" + tmp3 = _mm256_permute2x128_si256(src1, src1, 0x11); // copy high 128-bit to low from "(src >> 4) & 0x0F" + + tmp0 = _mm256_shuffle_epi8(tbl0, tmp0); // table look-up + tmp1 = _mm256_shuffle_epi8(tbl1, tmp1); + tmp2 = _mm256_shuffle_epi8(tbl2, tmp2); + tmp3 = _mm256_shuffle_epi8(tbl3, tmp3); + + tmp0 = _mm256_xor_si256(tmp0, tmp1); // combine result + tmp2 = _mm256_xor_si256(tmp2, tmp3); + dest = _mm256_xor_si256(dest, tmp0); + dest = _mm256_xor_si256(dest, tmp2); + _mm256_store_si256((__m256i *)output, dest); // write dest 32-bytes + + input += 32; + output += 32; + bsize -= 32; + } + + // AVX-SSE 切り替えの回避 + _mm256_zeroupper(); +} +*/ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// データを並び替えることで、メモリーアクセスを高速化する +void galois_altmap32_change(unsigned char *data, unsigned int bsize) +{ + __m128i xmm0, xmm1, xmm2, xmm3, mask; + + mask = _mm_setzero_si128(); + mask = _mm_cmpeq_epi16(mask, mask); // 0xFFFF *8 + mask = _mm_srli_epi16(mask, 8); // 0x00FF *8 + + while (bsize != 0){ + xmm1 = _mm_load_si128((__m128i *)data); // read 32-bytes + xmm3 = _mm_load_si128((__m128i *)data + 1); + + xmm0 = _mm_and_si128(xmm1, mask); // erase higher byte + xmm2 = _mm_and_si128(xmm3, mask); + xmm1 = _mm_srli_epi16(xmm1, 8); // move higher byte to lower + xmm3 = _mm_srli_epi16(xmm3, 8); + xmm0 = _mm_packus_epi16(xmm0, xmm2); // select lower byte of each word + xmm1 = _mm_packus_epi16(xmm1, xmm3); // select higher byte of each word + + _mm_store_si128((__m128i *)data, xmm0); // write 32-bytes + _mm_store_si128((__m128i *)data + 1, xmm1); + + data += 32; + bsize -= 32; + } +} + +// データの並びを元に戻す +void galois_altmap32_return(unsigned char *data, unsigned int bsize) +{ + __m128i xmm0, xmm1, xmm2; + + while (bsize != 0){ + xmm1 = _mm_load_si128((__m128i *)data); // read 32-bytes + xmm2 = _mm_load_si128((__m128i *)data + 1); + + xmm0 = _mm_unpacklo_epi8(xmm1, xmm2); // interleave lower and higher bytes + xmm1 = _mm_unpackhi_epi8(xmm1, xmm2); + + _mm_store_si128((__m128i *)data, xmm0); // write 32-bytes + _mm_store_si128((__m128i *)data + 1, xmm1); + + data += 32; + bsize -= 32; + } +} + +// 並び替えない場合 +void galois_altmap_none(unsigned char *data, unsigned int bsize) +{ +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* +from ParPar; "gf_w16_additions.c" +gf_w16_xor_lazy_sse_altmap_multiply_region +gf_w16_xor_lazy_sse_jit_altmap_multiply_region +*/ + +// 256バイトごとにビット単位の XOR で計算する方法 +// input と output の領域は重ならないようにすること +static void gf16_sse2_block256(unsigned char *input, unsigned char *output, unsigned int bsize, int factor) +{ + unsigned int i, bit; + unsigned int counts[16]; + uintptr_t deptable[16][16]; + __m128i depmask1, depmask2, polymask1, polymask2, addvals1, addvals2; + unsigned short tmp_depmask[16]; + + // calculate dependent bits + addvals1 = _mm_set_epi16(1<< 7, 1<< 6, 1<< 5, 1<< 4, 1<< 3, 1<< 2, 1<<1, 1<<0); + addvals2 = _mm_set_epi16(1<<15, 1<<14, 1<<13, 1<<12, 1<<11, 1<<10, 1<<9, 1<<8); + + // duplicate each bit in the polynomial 16 times + polymask2 = _mm_set1_epi16(PRIM_POLY & 0xFFFF); // chop off top bit, although not really necessary + polymask1 = _mm_and_si128(polymask2, _mm_set_epi16(1<< 8, 1<< 9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15)); + polymask2 = _mm_and_si128(polymask2, _mm_set_epi16(1<< 0, 1<< 1, 1<< 2, 1<< 3, 1<< 4, 1<< 5, 1<< 6, 1<< 7)); + polymask1 = _mm_cmpeq_epi16(_mm_setzero_si128(), polymask1); + polymask2 = _mm_cmpeq_epi16(_mm_setzero_si128(), polymask2); + + if (factor & (1<<15)){ + // XOR + depmask1 = addvals1; + depmask2 = addvals2; + } else { + depmask1 = _mm_setzero_si128(); + depmask2 = _mm_setzero_si128(); + } + for (i = (1<<14); i; i >>= 1){ + // rotate + __m128i last = _mm_shuffle_epi32(_mm_shufflelo_epi16(depmask1, 0), 0); + depmask1 = _mm_insert_epi16( + _mm_srli_si128(depmask1, 2), + _mm_extract_epi16(depmask2, 0), + 7 + ); + depmask2 = _mm_srli_si128(depmask2, 2); + + // XOR poly + depmask1 = _mm_xor_si128(depmask1, _mm_andnot_si128(polymask1, last)); + depmask2 = _mm_xor_si128(depmask2, _mm_andnot_si128(polymask2, last)); + + if (factor & i){ + // XOR + depmask1 = _mm_xor_si128(depmask1, addvals1); + depmask2 = _mm_xor_si128(depmask2, addvals2); + } + } + + // generate needed tables + _mm_storeu_si128((__m128i*)(tmp_depmask), depmask1); + _mm_storeu_si128((__m128i*)(tmp_depmask + 8), depmask2); + for (bit = 0; bit < 16; bit++){ + unsigned int cnt = 0; + for (i = 0; i < 16; i++){ + if (tmp_depmask[bit] & (1<>= 1){ + // rotate + __m128i last = _mm_shuffle_epi32(_mm_shufflelo_epi16(depmask1, 0), 0); + depmask1 = _mm_insert_epi16( + _mm_srli_si128(depmask1, 2), + _mm_extract_epi16(depmask2, 0), + 7 + ); + depmask2 = _mm_srli_si128(depmask2, 2); + + // XOR poly + depmask1 = _mm_xor_si128(depmask1, _mm_andnot_si128(polymask1, last)); + depmask2 = _mm_xor_si128(depmask2, _mm_andnot_si128(polymask2, last)); + + if (factor & i){ + // XOR + depmask1 = _mm_xor_si128(depmask1, addvals1); + depmask2 = _mm_xor_si128(depmask2, addvals2); + } + } + + // attempt to remove some redundant XOR ops with a simple heuristic + // heuristic: we just find common XOR elements between bit pairs + { + __m128i tmp1, tmp2; + // first, we need to re-arrange words so that we can perform bitwise AND on neighbouring pairs + // unfortunately, PACKUSDW is SSE4.1 only, so emulate it with shuffles + // 01234567 -> 02461357 + tmp1 = _mm_shuffle_epi32( + _mm_shufflelo_epi16( + _mm_shufflehi_epi16(depmask1, 0xD8), /* 0xD8 == 0b11011000 */ + 0xD8 + ), + 0xD8 + ); + tmp2 = _mm_shuffle_epi32( + _mm_shufflelo_epi16( + _mm_shufflehi_epi16(depmask2, 0xD8), + 0xD8 + ), + 0xD8 + ); + common_mask = _mm_and_si128( + // [02461357, 8ACE9BDF] -> [02468ACE, 13579BDF] + _mm_unpacklo_epi64(tmp1, tmp2), + _mm_unpackhi_epi64(tmp1, tmp2) + ); + // we have the common elements between pairs, but it doesn't make sense to process a separate queue if there's only one common element (0 XORs), so eliminate those + common_mask = _mm_andnot_si128(_mm_cmpeq_epi16( + _mm_setzero_si128(), + // "(v & (v-1)) == 0" is true if only zero/one bit is set in each word + _mm_and_si128(common_mask, _mm_sub_epi16(common_mask, _mm_set1_epi16(1))) + ), common_mask); + // we now have a common elements mask without 1-bit words, just simply merge stuff in + depmask1 = _mm_xor_si128(depmask1, _mm_unpacklo_epi16(common_mask, common_mask)); + depmask2 = _mm_xor_si128(depmask2, _mm_unpackhi_epi16(common_mask, common_mask)); + _mm_storeu_si128((__m128i*)common_depmask, common_mask); + } + + _mm_storeu_si128((__m128i*)(tmp_depmask), depmask1); + _mm_storeu_si128((__m128i*)(tmp_depmask + 8), depmask2); + + // Multi-threading だとスレッドごとに実行領域を分離しないとアクセス違反エラーが発生する + thread_id = GetCurrentThreadId(); // 自分のスレッド ID を取得する + for (j = 0; j < MAX_CPU; j++){ // 対応するスレッド個数は MAX_CPU 個まで + if (jit_id[j] == thread_id) + break; + } + if (j == MAX_CPU){ // 初期状態では jit_code 内は全て 0 なので jit_id も 0 だけ + for (j = 0; j < MAX_CPU; j++){ + if (InterlockedCompareExchange(jit_id + j, thread_id, 0) == 0) // 0と置き換えたなら + break; + } + } + jit_exec = jit_code + 4096 * j; + jit_ptr = jit_exec; + +#ifdef _WIN64 + _jit_push(&jit_ptr, BP); + _jit_mov_r(&jit_ptr, BP, SP); + // align pointer (avoid SP because stuff is encoded differently with it) + _jit_mov_r(&jit_ptr, AX, SP); + _jit_and_i(&jit_ptr, AX, 0xF); + _jit_sub_r(&jit_ptr, BP, AX); + + // make Windows happy and save XMM6-15 registers + // ideally should be done by this function, not JIT code, but MSVC has a convenient policy of no inline ASM + for (i = 6; i < 16; i++) + _jit_movaps_store(&jit_ptr, BP, -((int32_t)i-5)*16, (uint8_t)i); +#endif + + // adding 128 to the destination pointer allows the register offset to be coded in 1 byte + // eg: 'movdqa xmm0, [rdx+0x90]' is 8 bytes, whilst 'movdqa xmm0, [rdx-0x60]' is 5 bytes + _jit_mov_i(&jit_ptr, AX, (intptr_t)input + 128); + _jit_mov_i(&jit_ptr, DX, (intptr_t)output + 128); + _jit_mov_i(&jit_ptr, CX, (intptr_t)output + bsize + 128); + + _jit_align32(&jit_ptr); + pos_startloop = jit_ptr; + + //_jit_movaps_load(reg, xreg, offs) + // (we just save a conditional by hardcoding this) + #define _LD_APS(xreg, mreg, offs) \ + *(int32_t*)(jit_ptr) = 0x40280F + ((xreg) <<19) + ((mreg) <<16) + (((offs)&0xFF) <<24); \ + jit_ptr += 4 + #define _ST_APS(mreg, offs, xreg) \ + *(int32_t*)(jit_ptr) = 0x40290F + ((xreg) <<19) + ((mreg) <<16) + (((offs)&0xFF) <<24); \ + jit_ptr += 4 + #define _LD_APS64(xreg, mreg, offs) \ + *(int64_t*)(jit_ptr) = 0x40280F44 + ((xreg-8) <<27) + ((mreg) <<24) + ((int64_t)((offs)&0xFF) <<32); \ + jit_ptr += 5 + #define _ST_APS64(mreg, offs, xreg) \ + *(int64_t*)(jit_ptr) = 0x40290F44 + ((xreg-8) <<27) + ((mreg) <<24) + ((int64_t)((offs)&0xFF) <<32); \ + jit_ptr += 5 + +#ifdef _WIN64 + #define _LD_DQA(xreg, mreg, offs) \ + *(int64_t*)(jit_ptr) = 0x406F0F66 + ((xreg) <<27) + ((mreg) <<24) + ((int64_t)((offs)&0xFF) <<32); \ + jit_ptr += 5 + #define _ST_DQA(mreg, offs, xreg) \ + *(int64_t*)(jit_ptr) = 0x407F0F66 + ((xreg) <<27) + ((mreg) <<24) + ((int64_t)((offs)&0xFF) <<32); \ + jit_ptr += 5 +#else + #define _LD_DQA(xreg, mreg, offs) \ + *(int32_t*)(jit_ptr) = 0x406F0F66 + ((xreg) <<27) + ((mreg) <<24); \ + *(jit_ptr +4) = (uint8_t)((offs)&0xFF); \ + jit_ptr += 5 + #define _ST_DQA(mreg, offs, xreg) \ + *(int32_t*)(jit_ptr) = 0x407F0F66 + ((xreg) <<27) + ((mreg) <<24); \ + *(jit_ptr +4) = (uint8_t)((offs)&0xFF); \ + jit_ptr += 5 +#endif + #define _LD_DQA64(xreg, mreg, offs) \ + *(int64_t*)(jit_ptr) = 0x406F0F4466 + ((int64_t)(xreg-8) <<35) + ((int64_t)(mreg) <<32) + ((int64_t)((offs)&0xFF) <<40); \ + jit_ptr += 6 + #define _ST_DQA64(mreg, offs, xreg) \ + *(int64_t*)(jit_ptr) = 0x407F0F4466 + ((int64_t)(xreg-8) <<35) + ((int64_t)(mreg) <<32) + ((int64_t)((offs)&0xFF) <<40); \ + jit_ptr += 6 + + //_jit_xorps_m(reg, AX, offs<<4); + #define _XORPS_M_(reg, offs, tr) \ + *(int32_t*)(jit_ptr) = (0x40570F + ((reg) << 19) + (((offs)&0xFF) <<28)) ^ (tr) + #define _C_XORPS_M(reg, offs, c) \ + _XORPS_M_(reg, offs, 0); \ + jit_ptr += (c)<<2 + #define _XORPS_M64_(reg, offs, tr) \ + *(int64_t*)(jit_ptr) = (0x40570F44 + (((reg)-8) << 27) + ((int64_t)((offs)&0xFF) <<36)) ^ ((tr)<<8) + #define _C_XORPS_M64(reg, offs, c) \ + _XORPS_M64_(reg, offs, 0); \ + jit_ptr += ((c)<<2)+(c) + + //_jit_pxor_m(1, AX, offs<<4); +#ifdef _WIN64 + #define _PXOR_M_(reg, offs, tr) \ + *(int64_t*)(jit_ptr) = (0x40EF0F66 + ((reg) << 27) + ((int64_t)((offs)&0xFF) << 36)) ^ (tr) +#else + #define _PXOR_M_(reg, offs, tr) \ + *(int32_t*)(jit_ptr) = (0x40EF0F66 + ((reg) << 27)) ^ (tr); \ + *(jit_ptr +4) = (uint8_t)(((offs)&0xFF) << 4) +#endif + #define _PXOR_M(reg, offs) \ + _PXOR_M_(reg, offs, 0); \ + jit_ptr += 5 + #define _C_PXOR_M(reg, offs, c) \ + _PXOR_M_(reg, offs, 0); \ + jit_ptr += ((c)<<2)+(c) + #define _PXOR_M64_(reg, offs, tr) \ + *(int64_t*)(jit_ptr) = (0x40EF0F4466 + ((int64_t)((reg)-8) << 35) + ((int64_t)((offs)&0xFF) << 44)) ^ ((tr)<<8) + #define _C_PXOR_M64(reg, offs, c) \ + _PXOR_M64_(reg, offs, 0); \ + jit_ptr += ((c)<<2)+((c)<<1) + + //_jit_xorps_r(r2, r1) + #define _XORPS_R_(r2, r1, tr) \ + *(int32_t*)(jit_ptr) = (0xC0570F + ((r2) <<19) + ((r1) <<16)) ^ (tr) + #define _XORPS_R(r2, r1) \ + _XORPS_R_(r2, r1, 0); \ + jit_ptr += 3 + #define _C_XORPS_R(r2, r1, c) \ + _XORPS_R_(r2, r1, 0); \ + jit_ptr += ((c)<<1)+(c) + // r2 is always < 8, r1 here is >= 8 + #define _XORPS_R64_(r2, r1, tr) \ + *(int32_t*)(jit_ptr) = (0xC0570F41 + ((r2) <<27) + ((r1) <<24)) ^ ((tr)<<8) + #define _C_XORPS_R64(r2, r1, c) \ + _XORPS_R64_(r2, r1, 0); \ + jit_ptr += (c)<<2 + + //_jit_pxor_r(r2, r1) + #define _PXOR_R_(r2, r1, tr) \ + *(int32_t*)(jit_ptr) = (0xC0EF0F66 + ((r2) <<27) + ((r1) <<24)) ^ (tr) + #define _PXOR_R(r2, r1) \ + _PXOR_R_(r2, r1, 0); \ + jit_ptr += 4 + #define _C_PXOR_R(r2, r1, c) \ + _PXOR_R_(r2, r1, 0); \ + jit_ptr += (c)<<2 + #define _PXOR_R64_(r2, r1, tr) \ + *(int64_t*)(jit_ptr) = (0xC0EF0F4166 + ((int64_t)(r2) <<35) + ((int64_t)(r1) <<32)) ^ (((int64_t)tr)<<8) + #define _C_PXOR_R64(r2, r1, c) \ + _PXOR_R64_(r2, r1, 0); \ + jit_ptr += ((c)<<2)+(c) + + // optimised mix of xor/mov operations + #define _MOV_OR_XOR_FP_M(reg, offs, flag, c) \ + _XORPS_M_(reg, offs, flag); \ + flag &= (c)-1; \ + jit_ptr += (c)<<2 + #define _MOV_OR_XOR_FP_M64(reg, offs, flag, c) \ + _XORPS_M64_(reg, offs, flag); \ + flag &= (c)-1; \ + jit_ptr += ((c)<<2)+(c) + #define _MOV_OR_XOR_FP_INIT (0x570F ^ 0x280F) + + #define _MOV_OR_XOR_INT_M(reg, offs, flag, c) \ + _PXOR_M_(reg, offs, flag); \ + flag &= (c)-1; \ + jit_ptr += ((c)<<2)+(c) + #define _MOV_OR_XOR_INT_M64(reg, offs, flag, c) \ + _PXOR_M64_(reg, offs, flag); \ + flag &= (c)-1; \ + jit_ptr += ((c)<<2)+((c)<<1) + #define _MOV_OR_XOR_INT_INIT (0xEF0F00 ^ 0x6F0F00) + + #define _MOV_OR_XOR_R_FP(r2, r1, flag, c) \ + _XORPS_R_(r2, r1, flag); \ + flag &= (c)-1; \ + jit_ptr += ((c)<<1)+(c) + #define _MOV_OR_XOR_R64_FP(r2, r1, flag, c) \ + _XORPS_R64_(r2, r1, flag); \ + flag &= (c)-1; \ + jit_ptr += (c)<<2 + + #define _MOV_OR_XOR_R_INT(r2, r1, flag, c) \ + _PXOR_R_(r2, r1, flag); \ + flag &= (c)-1; \ + jit_ptr += (c)<<2 + #define _MOV_OR_XOR_R64_INT(r2, r1, flag, c) \ + _PXOR_R64_(r2, r1, flag); \ + flag &= (c)-1; \ + jit_ptr += ((c)<<2)+(c) + + // generate code +#ifdef _WIN64 + // preload upper 13 inputs into registers + #define _XORS_FROM_MEMORY 3 + for (inBit = 3; inBit < 8; inBit++){ + _LD_APS(inBit, AX, (inBit-8)<<4); + } + for (; inBit<16; inBit++){ + _LD_APS64(inBit, AX, (inBit-8)<<4); + } +#else + // can only fit 5 in 32-bit mode :( + #define _XORS_FROM_MEMORY 11 + for (inBit = 3; inBit < 8; inBit++){ // despite appearances, we're actually loading the top 5, not mid 5 + _LD_APS(inBit, AX, inBit<<4); + } +#endif + for (bit = 0; bit < 16; bit += 2){ + int destOffs = (int)((bit<<4)-128); + FAST_U32 movC = _MOV_OR_XOR_INT_INIT; + FAST_U16 mask1 = tmp_depmask[bit], mask2 = tmp_depmask[bit+1], maskC = common_depmask[bit>>1]; + _LD_APS(0, DX, destOffs); + _LD_DQA(1, DX, destOffs+16); + + for (inBit = -8; inBit < (_XORS_FROM_MEMORY-8); inBit++){ + _MOV_OR_XOR_INT_M(2, inBit, movC, maskC & 1); + _C_XORPS_M(0, inBit, mask1 & 1); + _C_PXOR_M(1, inBit, mask2 & 1); + mask1 >>= 1; + mask2 >>= 1; + maskC >>= 1; + } + // at least 5 can come from registers + for (inBit = 3; inBit < 8; inBit++){ + _MOV_OR_XOR_R_INT(2, inBit, (int32_t)movC, maskC & 1); + _C_XORPS_R(0, inBit, mask1 & 1); + _C_PXOR_R(1, inBit, mask2 & 1); + mask1 >>= 1; + mask2 >>= 1; + maskC >>= 1; + } +#ifdef _WIN64 + // more XORs can come from 64-bit registers + for (inBit = 0; inBit < 8; inBit++){ + _MOV_OR_XOR_R64_INT(2, inBit, movC, maskC & 1); + _C_XORPS_R64(0, inBit, mask1 & 1); + _C_PXOR_R64(1, inBit, mask2 & 1); + mask1 >>= 1; + mask2 >>= 1; + maskC >>= 1; + } +#endif + if (!movC){ + _XORPS_R(0, 2); + _PXOR_R(1, 2); // penalty? + } + _ST_APS(DX, destOffs, 0); + _ST_DQA(DX, destOffs+16, 1); + } + #undef _XORS_FROM_MEMORY + + _jit_add_i(&jit_ptr, AX, 256); + _jit_add_i(&jit_ptr, DX, 256); + + _jit_cmp_r(&jit_ptr, DX, CX); + _jit_jcc(&jit_ptr, JL, pos_startloop); + +#ifdef _WIN64 + for (i = 6; i < 16; i++) + _jit_movaps_load(&jit_ptr, (uint8_t)i, BP, -((int32_t)i-5)*16); + _jit_pop(&jit_ptr, BP); +#endif + + _jit_ret(&jit_ptr); + + // exec + (*(void(*)(void))jit_exec)(); + +} + +/* +XOR method is based on ParPar. +https://github.com/animetosho/ParPar/ +*/ + +// bit 単位で XOR するには、256バイトごとに並び替える +void galois_altmap256_change(unsigned char *data, unsigned int bsize) +{ + unsigned short dtmp[128]; + int i, j; + __m128i ta, tb, lmask, th, tl; + + lmask = _mm_set1_epi16(0xff); + + while (bsize != 0){ + for (j = 0; j < 8; j++){ + ta = _mm_load_si128((__m128i *)data); // read 32-bytes + tb = _mm_load_si128((__m128i *)data + 1); + + // split to high/low parts + th = _mm_packus_epi16(_mm_srli_epi16(tb, 8), _mm_srli_epi16(ta, 8)); + tl = _mm_packus_epi16(_mm_and_si128(tb, lmask), _mm_and_si128(ta, lmask)); + + // save to dest by extracting 16-bit masks + dtmp[0 + j] = _mm_movemask_epi8(th); + for (i = 1; i < 8; i++){ + th = _mm_slli_epi16(th, 1); // byte shift would be nicer, but ultimately doesn't matter here + dtmp[i*8 + j] = _mm_movemask_epi8(th); + } + dtmp[64 + j] = _mm_movemask_epi8(tl); + for (i = 1; i < 8; i++){ + tl = _mm_slli_epi16(tl, 1); + dtmp[64 + i*8 + j] = _mm_movemask_epi8(tl); + } + data += 32; + } + // we only really need to copy temp -> dest + memcpy(data - 256, dtmp, 256); + bsize -= 256; + } +} + +void galois_altmap256_return(unsigned char *data, unsigned int bsize) +{ + unsigned short dtmp[128]; + int i, j; + __m128i ta, tb, lmask, th, tl; + + th = _mm_setzero_si128(); // shut up compiler warning + tl = _mm_setzero_si128(); + lmask = _mm_set1_epi16(0xff); + + while (bsize != 0){ + for (j = 0; j < 8; j++){ + // load in pattern: [0011223344556677] [8899AABBCCDDEEFF] + tl = _mm_insert_epi16(tl, *(int *)(data + 240), 0); + th = _mm_insert_epi16(th, *(int *)(data + 112), 0); + tl = _mm_insert_epi16(tl, *(int *)(data + 224), 1); + th = _mm_insert_epi16(th, *(int *)(data + 96), 1); + tl = _mm_insert_epi16(tl, *(int *)(data + 208), 2); + th = _mm_insert_epi16(th, *(int *)(data + 80), 2); + tl = _mm_insert_epi16(tl, *(int *)(data + 192), 3); + th = _mm_insert_epi16(th, *(int *)(data + 64), 3); + tl = _mm_insert_epi16(tl, *(int *)(data + 176), 4); + th = _mm_insert_epi16(th, *(int *)(data + 48), 4); + tl = _mm_insert_epi16(tl, *(int *)(data + 160), 5); + th = _mm_insert_epi16(th, *(int *)(data + 32), 5); + tl = _mm_insert_epi16(tl, *(int *)(data + 144), 6); + th = _mm_insert_epi16(th, *(int *)(data + 16), 6); + tl = _mm_insert_epi16(tl, *(int *)(data + 128), 7); + th = _mm_insert_epi16(th, *(int *)data, 7); + + // swizzle to [0123456789ABCDEF] [0123456789ABCDEF] + ta = _mm_packus_epi16(_mm_srli_epi16(tl, 8),_mm_srli_epi16(th, 8)); + tb = _mm_packus_epi16(_mm_and_si128(tl, lmask),_mm_and_si128(th, lmask)); + + // extract top bits + dtmp[j*16 + 7] = _mm_movemask_epi8(ta); + dtmp[j*16 + 15] = _mm_movemask_epi8(tb); + for (i = 1; i < 8; i++){ + ta = _mm_slli_epi16(ta, 1); + tb = _mm_slli_epi16(tb, 1); + dtmp[j*16 + 7-i] = _mm_movemask_epi8(ta); + dtmp[j*16 + 15-i] = _mm_movemask_epi8(tb); + } + data += 2; + } + + // we only really need to copy temp -> dest + memcpy(data - 16, dtmp, 256); + data += 240; + bsize -= 256; + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 複数のテーブルを使って掛け算する +void galois_region_multiply( + unsigned short *r1, // Region to multiply + unsigned short *r2, // Products go here + unsigned int count, // Count of number in short + int factor) // Number to multiply by +{ + if (factor <= 1){ + if (factor == 0) + return; + + // アドレスが 4の倍数で無い場合は 4バイト単位で計算する効率が落ちる + if ((ULONG_PTR)r2 & 2){ + // そこで最初の 1個(2バイト)だけ普通に計算する + *r2 ^= *r1; + r1++; + r2++; + count--; + } + + while (count >= 2){ // 2個(4バイト)ずつ計算する + ((unsigned int *)r2)[0] ^= ((unsigned int *)r1)[0]; + r1 += 2; + r2 += 2; + count -= 2; + } + if (count == 1) // 最後に余ったやつだけ普通に計算する + *r2 ^= *r1; + return; + } + + if (count >= 32){ // 64バイト以上なら掛け算用のテーブルを使った方が速い +#ifndef NO_SIMD + if (cpu_flag & 1){ // SSSE3 対応なら + __declspec( align(16) ) unsigned char small_table[128]; + int s, d; + + create_eight_table(small_table, factor); + + // アドレスが 16の倍数で無い場合は 16バイト単位で計算する効率が落ちる + while ((ULONG_PTR)r2 & 0xE){ + // そこで最初の 1~7個(2~14バイト)だけ普通に計算する + s = r1[0]; + d = r2[0]; + d ^= small_table[s & 0xF] | ((int)(small_table[16 + (s & 0xF)]) << 8); + s = s >> 4; + d ^= small_table[32 + (s & 0xF)] | ((int)(small_table[48 + (s & 0xF)]) << 8); + s = s >> 4; + d ^= small_table[64 + (s & 0xF)] | ((int)(small_table[80 + (s & 0xF)]) << 8); + s = s >> 4; + d ^= small_table[96 + s] | ((int)(small_table[112 + s]) << 8); + r2[0] = (unsigned short)d; + r1++; + r2++; + count--; + } + + if (sse_unit == 16){ + // 8個ずつ計算するので 8の倍数にする + gf16_ssse3_block16u((unsigned char *)r1, (unsigned char *)r2, + (count & 0xFFFFFFF8) << 1, small_table); + r1 += count & 0xFFFFFFF8; + r2 += count & 0xFFFFFFF8; + count &= 7; + } else { + // 16個ずつ計算するので 16の倍数にする + gf16_ssse3_block32u((unsigned char *)r1, (unsigned char *)r2, + (count & 0xFFFFFFF0) << 1, small_table); + r1 += count & 0xFFFFFFF0; + r2 += count & 0xFFFFFFF0; + count &= 15; + } + + // 残りは 1個ずつ計算する + while (count != 0){ + s = r1[0]; + d = r2[0]; + d ^= small_table[s & 0xF] | ((int)(small_table[16 + (s & 0xF)]) << 8); + s = s >> 4; + d ^= small_table[32 + (s & 0xF)] | ((int)(small_table[48 + (s & 0xF)]) << 8); + s = s >> 4; + d ^= small_table[64 + (s & 0xF)] | ((int)(small_table[80 + (s & 0xF)]) << 8); + s = s >> 4; + d ^= small_table[96 + s] | ((int)(small_table[112 + s]) << 8); + r2[0] = (unsigned short)d; + r1++; + r2++; + count--; + } + + } else { // Combined Multi Table support (2 tables of 256-entries) +#endif + unsigned int mtab[256 * 2]; + + create_two_table(mtab, factor); // build combined multiplication tables + + // アドレスが 8の倍数で無い場合は 8バイト単位で計算する効率が落ちる + while ((ULONG_PTR)r2 & 6){ + // そこで最初の 1~3個(2~6バイト)だけ普通に計算する + r2[0] ^= mtab[((unsigned char *)r1)[0]] ^ mtab[256 + ((unsigned char *)r1)[1]]; + r1++; + r2++; + count--; + } + +#ifndef _WIN64 // 32-bit 版なら MMX を使う +#ifndef NO_SIMD + // 4個(8バイト)ずつ計算するので 4の倍数にする + DoBlock8((unsigned char *)r1, (unsigned char *)r2, (count & 0xFFFFFFFC) << 1, mtab); + r1 += count & 0xFFFFFFFC; + r2 += count & 0xFFFFFFFC; + count &= 3; + +#else // MMX を使わないなら + // バッファーを 32-bit整数として扱う + while (count >= 2){ // 2個(4バイト)ずつ計算する + // 先に計算しておいた 2個の参照テーブルを使う + ((unsigned int *)r2)[0] ^= mtab[((unsigned char *)r1)[0]] ^ mtab[256 + ((unsigned char *)r1)[1]] ^ + ((mtab[((unsigned char *)r1)[2]] ^ mtab[256 + ((unsigned char *)r1)[3]]) << 16); + r1 += 2; + r2 += 2; + count -= 2; + } +#endif + +#else // 64-bit 版なら 64-bit 整数を使う + // バッファーを 64-bit整数として扱う + while (count >= 4){ // 4個(8バイト)ずつ計算する + // 先に計算しておいた 2個の参照テーブルを使う + ((unsigned __int64 *)r2)[0] ^= + mtab[((unsigned char *)r1)[0]] ^ mtab[256 + ((unsigned char *)r1)[1]] ^ + ((mtab[((unsigned char *)r1)[2]] ^ mtab[256 + ((unsigned char *)r1)[3]]) << 16) ^ + ((unsigned __int64)(mtab[((unsigned char *)r1)[4]] ^ mtab[256 + ((unsigned char *)r1)[5]]) << 32) ^ + ((unsigned __int64)(mtab[((unsigned char *)r1)[6]] ^ mtab[256 + ((unsigned char *)r1)[7]]) << 48); + r1 += 4; + r2 += 4; + count -= 4; + } +#endif + + // 残りは 1個ずつ計算する + while (count != 0){ + r2[0] ^= mtab[((unsigned char *)r1)[0]] ^ mtab[256 + ((unsigned char *)r1)[1]]; + r1++; + r2++; + count--; + } +#ifndef NO_SIMD + } +#endif + + } else { // 小さいデータは普通に計算する + int log_y = galois_log_table[factor]; + + while (count != 0){ + r2[0] ^= galois_multiply_fix(r1[0], log_y); + r1++; + r2++; + count--; + } + } +} + +// 行列の割り算用、微妙に速いけど大差無し +void galois_region_divide( + unsigned short *r1, // Region to divide. products go here + unsigned int count, // Count of number in short + int factor) // Number to divide by +{ + factor = galois_reciprocal(factor); // factor = 1 / factor + + if (count >= 32){ + unsigned int mtab[256 * 2]; + + create_two_table(mtab, factor); // 掛け算用のテーブルをその場で構成する + + // アドレスが 4の倍数で無い場合は 4バイト単位で計算する効率が落ちる + if (((ULONG_PTR)r1 & 2) != 0){ + // そこで最初の 1個(2バイト)だけ普通に計算する + r1[0] = (unsigned short)(mtab[((unsigned char *)r1)[0]] ^ mtab[256 + ((unsigned char *)r1)[1]]); + r1++; + count--; + } + + // バッファーを 32-bit整数として扱う + while (count >= 2){ // 2個(4バイト)ずつ計算する + // 先に計算しておいた 2個の参照テーブルを使う + ((unsigned int *)r1)[0] = mtab[((unsigned char *)r1)[0]] ^ mtab[256 + ((unsigned char *)r1)[1]] ^ + ((mtab[((unsigned char *)r1)[2]] ^ mtab[256 + ((unsigned char *)r1)[3]]) << 16); + r1 += 2; + count -= 2; + } + // 奇数なら最後に 1個余る + if (count == 1) + r1[0] = (unsigned short)(mtab[((unsigned char *)r1)[0]] ^ mtab[256 + ((unsigned char *)r1)[1]]); + + } else { // 小さいデータは普通に計算する + int log_y = galois_log_table[factor]; + + while (count != 0){ + r1[0] = galois_multiply_fix(r1[0], log_y); + r1++; + count--; + } + } +} + +// 16バイト境界のバッファー専用のXOR +void galois_align_xor( + unsigned char *r1, // Region to multiply + unsigned char *r2, // Products go here + unsigned int len) // Byte length +{ +#ifndef NO_SIMD + __m128i xmm0, xmm1; // 16バイトごとに XOR する + + while (len != 0){ + xmm0 = _mm_load_si128((__m128i *)r1); + xmm1 = _mm_load_si128((__m128i *)r2); + xmm1 = _mm_xor_si128(xmm1, xmm0); + _mm_store_si128((__m128i *)r2, xmm1); + r1 += 16; + r2 += 16; + len -= 16; + } + +#else // SSE2 を使わないなら + while (len != 0){ // 4バイトずつ計算する + ((unsigned int *)r2)[0] ^= ((unsigned int *)r1)[0]; + r1 += 4; + r2 += 4; + len -= 4; + } +#endif +} + +// 16バイト境界のバッファー専用の掛け算 +void galois_align16_multiply( + unsigned char *r1, // Region to multiply (must be aligned by 16) + unsigned char *r2, // Products go here + unsigned int len, // Byte length (must be multiple of 32) + int factor) // Number to multiply by +{ + if (factor <= 1){ + if (factor == 0) + return; + +#ifndef NO_SIMD + { __m128i xmm0, xmm1; // 16バイトごとに XOR する + while (len != 0){ + xmm0 = _mm_load_si128((__m128i *)r1); + xmm1 = _mm_load_si128((__m128i *)r2); + xmm1 = _mm_xor_si128(xmm1, xmm0); + _mm_store_si128((__m128i *)r2, xmm1); + r1 += 16; + r2 += 16; + len -= 16; + } + } +#else // SSE2 を使わないなら + while (len != 0){ // 4バイトずつ計算する + ((unsigned int *)r2)[0] ^= ((unsigned int *)r1)[0]; + r1 += 4; + r2 += 4; + len -= 4; + } +#endif + + // 掛け算用のテーブルを常に作成する (32バイトだと少し遅くなる) +#ifndef NO_SIMD + } else if (cpu_flag & 1){ // SSSE3 対応なら + __declspec( align(16) ) unsigned char small_table[128]; + + create_eight_table(small_table, factor); + + gf16_ssse3_block16u(r1, r2, len, small_table); + // sse_unit が 32の倍数ならこちらでもいい + //gf16_ssse3_block32u(r1, r2, len, small_table); + +#endif + } else { // Combined Multi Table support (2 tables of 256-entries) + unsigned int mtab[256 * 2]; + + create_two_table(mtab, factor); // build combined multiplication tables + +#ifndef _WIN64 // 32-bit 版なら MMX を使う +#ifndef NO_SIMD + DoBlock8(r1, r2, len, mtab); // process large chunk 8-bytes a shot + +#else // MMX を使わないなら + // バッファーを 32-bit整数として扱う + while (len != 0){ // 4バイトずつ計算する + ((unsigned int *)r2)[0] ^= mtab[r1[0]] ^ mtab[256 + r1[1]] ^ + ((mtab[r1[2]] ^ mtab[256 + r1[3]]) << 16); + r1 += 4; + r2 += 4; + len -= 4; + } +#endif + +#else // 64-bit 版なら 64-bit 整数を使う + // バッファーを 64-bit整数として扱う + while (len != 0){ // 8バイトずつ計算する + ((unsigned __int64 *)r2)[0] ^= + mtab[r1[0]] ^ mtab[256 + r1[1]] ^ + ((mtab[r1[2]] ^ mtab[256 + r1[3]]) << 16) ^ + ((unsigned __int64)(mtab[r1[4]] ^ mtab[256 + r1[5]]) << 32) ^ + ((unsigned __int64)(mtab[r1[6]] ^ mtab[256 + r1[7]]) << 48); + r1 += 8; + r2 += 8; + len -= 8; + } +#endif + + } +} + +// 32バイトごとに並び替えられたバッファー専用の掛け算 (SSSE3 & ALTMAP) +void galois_align32_multiply( + unsigned char *r1, // Region to multiply (must be aligned by 16) + unsigned char *r2, // Products go here + unsigned int len, // Byte length (must be multiple of 32) + int factor) // Number to multiply by +{ + if (factor <= 1){ + if (factor != 0){ + __m128i xmm0, xmm1; // 16バイトごとに XOR する + + while (len != 0){ + xmm0 = _mm_load_si128((__m128i *)r1); + xmm1 = _mm_load_si128((__m128i *)r2); + xmm1 = _mm_xor_si128(xmm1, xmm0); + _mm_store_si128((__m128i *)r2, xmm1); + r1 += 16; + r2 += 16; + len -= 16; + } + } + + // 掛け算用のテーブルを常に作成する (32バイトだと少し遅くなる) + } else { + __declspec( align(16) ) unsigned char small_table[128]; + + create_eight_table(small_table, factor); + + gf16_ssse3_block32_altmap(r1, r2, len, small_table); + } +} + +// 256バイトごとに並び替えられたバッファー専用の JIT(SSE2) を使った掛け算 +void galois_align256_multiply( + unsigned char *r1, // Region to multiply (must be aligned by 16) + unsigned char *r2, // Products go here + unsigned int len, // Byte length (must be multiple of 32) + int factor) // Number to multiply by +{ + if (factor <= 1){ + if (factor != 0){ + __m128i xmm0, xmm1; // 16バイトごとに XOR する + + while (len != 0){ + xmm0 = _mm_load_si128((__m128i *)r1); + xmm1 = _mm_load_si128((__m128i *)r2); + xmm1 = _mm_xor_si128(xmm1, xmm0); + _mm_store_si128((__m128i *)r2, xmm1); + r1 += 16; + r2 += 16; + len -= 16; + } + } + + // 常に JIT(SSE2) を使う + } else { + gf16_sse2_block256_jit(r1, r2, len, factor); + } +} + +// 32バイトごとに並び替えられたバッファー専用の掛け算 (AVX2 & ALTMAP) +void galois_align32avx_multiply( + unsigned char *r1, // Region to multiply (must be aligned by 32) + unsigned char *r2, // Products go here + unsigned int len, // Byte length (must be multiple of 32) + int factor) // Number to multiply by +{ + if (factor <= 1){ + if (factor != 0){ + __m256i ymm0, ymm1; // 32バイトごとに XOR する + + while (len != 0){ + ymm0 = _mm256_load_si256((__m256i *)r1); + ymm1 = _mm256_load_si256((__m256i *)r2); + ymm1 = _mm256_xor_si256(ymm1, ymm0); + _mm256_store_si256((__m256i *)r2, ymm1); + r1 += 32; + r2 += 32; + len -= 32; + } + + _mm256_zeroupper(); // AVX-SSE 切り替えの回避 + +/* + __m128i xmm0, xmm1; // 16バイトごとに XOR する + + while (len != 0){ + xmm0 = _mm_load_si128((__m128i *)r1); + xmm1 = _mm_load_si128((__m128i *)r2); + xmm1 = _mm_xor_si128(xmm1, xmm0); + _mm_store_si128((__m128i *)r2, xmm1); + r1 += 16; + r2 += 16; + len -= 16; + } +*/ + } + + // 掛け算用のテーブルを常に作成する (32バイトだと少し遅くなる) + } else { + __declspec( align(32) ) unsigned char small_table[128]; + + create_eight_table(small_table, factor); + + gf16_avx2_block32(r1, r2, len, small_table); + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// チェックサムを計算する + +// buffer alignment must be 16, length must be multiple of 16 +void checksum16(unsigned char *data, unsigned char *hash, int byte_size) +{ + int i, count; + +#ifndef NO_SIMD // SSE2 を使うなら + __m128i temp16, prev16, data16, mask16, zero16, poly16; + + count = byte_size / 16; + prev16 = _mm_setzero_si128(); + zero16 = _mm_setzero_si128(); + poly16 = _mm_set1_epi32(0x100B100B); // PRIM_POLY = 0x1100B + + while (count > 0){ // HASH_RANGE バイトごとに + // 16バイトごとに XOR する + temp16 = _mm_setzero_si128(); + + if (count < HASH_RANGE / 16){ + i = count; + count = 0; + } else { + i = HASH_RANGE / 16; + count -= HASH_RANGE / 16; + } + while (i > 0){ + data16 = _mm_load_si128((__m128i *)data); // load 16-bytes + temp16 = _mm_xor_si128(temp16, data16); + data += 16; + i--; + } + + // 前回の値を 2倍して、今回の値を追加する + //temp16 = _mm_xor_si128(temp16, prev16); // 3倍する場合は、元の値も XOR すればいい + mask16 = _mm_cmpgt_epi16(zero16, prev16); // (0 > prev) ? 0xFFFF : 0x0000 + prev16 = _mm_slli_epi16(prev16, 1); // prev *= 2 + mask16 = _mm_and_si128(mask16, poly16); // 0x100B or 0x0000 + prev16 = _mm_xor_si128(prev16, mask16); + + prev16 = _mm_xor_si128(prev16, temp16); + } + _mm_store_si128((__m128i *)hash, prev16); + +#else // 4バイト整数で計算するなら + unsigned int *data4, temp[4], prev[4], x, mask; + + data4 = (unsigned int *)data; + count = byte_size / 4; + for (i = 0; i < 4; i++) + prev[i] = 0; + + while (count > 0){ // HASH_RANGE バイトごとに + // 4バイトごとに 16バイトに XOR する + for (i = 0; i < 4; i++) + temp[i] = 0; + for (i = 0; i < HASH_RANGE / 4; i++){ + temp[i & 3] ^= data4[i]; + count--; + if (count == 0) + break; + } + data4 += HASH_RANGE / 4; + + // 前回の値を 2倍して、今回の値を追加する + for (i = 0; i < 4; i++){ + x = prev[i]; + mask = (x & 0x80008000) >> 15; // 0x00010001 or 0x00000000 + x = (x & 0x7FFF7FFF) << 1; // 3倍する場合は「^=」にすればいい + x ^= mask * 0x100B; // 0x100B100B or 0x00000000 + + prev[i] = x ^ temp[i]; + } + } + for (i = 0; i < 4; i++) + ((unsigned int *)hash)[i] = prev[i]; +#endif +} + +// チェックサムを計算すると同時にデータを並び替える +// buffer alignment must be 16, length must be (multiple of 32) - 16 +void checksum16_altmap32(unsigned char *data, unsigned char *hash, int byte_size) +{ + int i, count; + __m128i temp16, prev16, mask16, zero16, poly16; + __m128i dataA, dataB, dataC, dataD, maskB; + + count = byte_size / 16; + prev16 = _mm_setzero_si128(); + zero16 = _mm_setzero_si128(); + poly16 = _mm_set1_epi32(0x100B100B); // PRIM_POLY = 0x1100B + maskB = _mm_set1_epi16(0x00FF); // 0x00FF *8 + + while (count > 0){ // HASH_RANGE バイトごとに + // 16バイトごとに XOR する + temp16 = _mm_setzero_si128(); + + if (count < HASH_RANGE / 16){ + i = count; + count = 0; + } else { + i = HASH_RANGE / 16; + count -= HASH_RANGE / 16; + } + while (i >= 2){ + dataA = _mm_load_si128((__m128i *)data); // read 32-bytes + dataB = _mm_load_si128((__m128i *)data + 1); + temp16 = _mm_xor_si128(temp16, dataA); + temp16 = _mm_xor_si128(temp16, dataB); + + dataC = _mm_and_si128(dataA, maskB); // erase higher byte + dataD = _mm_and_si128(dataB, maskB); + dataA = _mm_srli_epi16(dataA, 8); // move higher byte to lower + dataB = _mm_srli_epi16(dataB, 8); + dataC = _mm_packus_epi16(dataC, dataD); // select lower byte of each word + dataA = _mm_packus_epi16(dataA, dataB); // select higher byte of each word + + _mm_store_si128((__m128i *)data, dataC); // write 32-bytes + _mm_store_si128((__m128i *)data + 1, dataA); + + data += 32; + i -= 2; + } + if (i > 0){ + dataA = _mm_load_si128((__m128i *)data); // load 16-bytes + temp16 = _mm_xor_si128(temp16, dataA); + } + + // 前回の値を 2倍して、今回の値を追加する + //temp16 = _mm_xor_si128(temp16, prev16); // 3倍する場合は、元の値も XOR すればいい + mask16 = _mm_cmpgt_epi16(zero16, prev16); // (0 > prev) ? 0xFFFF : 0x0000 + prev16 = _mm_slli_epi16(prev16, 1); // prev *= 2 + mask16 = _mm_and_si128(mask16, poly16); // 0x100B or 0x0000 + prev16 = _mm_xor_si128(prev16, mask16); + + prev16 = _mm_xor_si128(prev16, temp16); + } + if (hash != data + 16) // ハッシュ値の保存先が別なら + _mm_store_si128((__m128i *)hash, prev16); + + // 最後にハッシュ値も並び替える + dataC = _mm_and_si128(dataA, maskB); // erase higher byte + dataD = _mm_and_si128(prev16, maskB); + dataA = _mm_srli_epi16(dataA, 8); // move higher byte to lower + dataB = _mm_srli_epi16(prev16, 8); + dataC = _mm_packus_epi16(dataC, dataD); // select lower byte of each word + dataA = _mm_packus_epi16(dataA, dataB); // select higher byte of each word + + _mm_store_si128((__m128i *)data, dataC); // write 32-bytes + _mm_store_si128((__m128i *)data + 1, dataA); +} + +// データの並びを元に戻すと同時にチェックサムを計算する +// buffer alignment must be 32, length must be (multiple of 32) - 16 +void checksum16_return32(unsigned char *data, unsigned char *hash, int byte_size) +{ + int i, count; + __m128i temp16, prev16, mask16, zero16, poly16; + __m128i dataA, dataB, dataC; + + count = byte_size / 16; + prev16 = _mm_setzero_si128(); + zero16 = _mm_setzero_si128(); + poly16 = _mm_set1_epi32(0x100B100B); // PRIM_POLY = 0x1100B + + while (count > 0){ // HASH_RANGE バイトごとに + // 16バイトごとに XOR する + temp16 = _mm_setzero_si128(); + + if (count < HASH_RANGE / 16){ + i = count; + count = 0; + } else { + i = HASH_RANGE / 16; + count -= HASH_RANGE / 16; + } + while (i >= 2){ + dataA = _mm_load_si128((__m128i *)data); // read 32-bytes + dataB = _mm_load_si128((__m128i *)data + 1); + + dataC = _mm_unpacklo_epi8(dataA, dataB); // interleave lower and higher bytes + dataA = _mm_unpackhi_epi8(dataA, dataB); + + _mm_store_si128((__m128i *)data, dataC); // write 32-bytes + _mm_store_si128((__m128i *)data + 1, dataA); + temp16 = _mm_xor_si128(temp16, dataC); + temp16 = _mm_xor_si128(temp16, dataA); + + data += 32; + i -= 2; + } + if (i > 0){ + dataA = _mm_load_si128((__m128i *)data); // read 32-bytes + dataB = _mm_load_si128((__m128i *)data + 1); + + dataC = _mm_unpacklo_epi8(dataA, dataB); // interleave lower and higher bytes + dataA = _mm_unpackhi_epi8(dataA, dataB); + + _mm_store_si128((__m128i *)data, dataC); // write 32-bytes + _mm_store_si128((__m128i *)data + 1, dataA); + temp16 = _mm_xor_si128(temp16, dataC); + } + + // 前回の値を 2倍して、今回の値を追加する + //temp16 = _mm_xor_si128(temp16, prev16); // 3倍する場合は、元の値も XOR すればいい + mask16 = _mm_cmpgt_epi16(zero16, prev16); // (0 > prev) ? 0xFFFF : 0x0000 + prev16 = _mm_slli_epi16(prev16, 1); // prev *= 2 + mask16 = _mm_and_si128(mask16, poly16); // 0x100B or 0x0000 + prev16 = _mm_xor_si128(prev16, mask16); + + prev16 = _mm_xor_si128(prev16, temp16); + } + + if (hash != data + 16) // ハッシュ値の保存先が別なら + _mm_store_si128((__m128i *)hash, prev16); +} + +// チェックサムを計算すると同時にデータを並び替える +// buffer alignment must be 256, length must be (multiple of 256) - 16 +void checksum16_altmap256(unsigned char *data, unsigned char *hash, int byte_size) +{ + // 順番に処理する場合 +// checksum16(data, hash, byte_size); +// galois_altmap256_change(data, byte_size + HASH_SIZE); + + unsigned short dtmp[128]; + int i, j; + __m128i ta, tb, lmask, th, tl, temp16, prev16, poly16; + + lmask = _mm_set1_epi16(0xff); + prev16 = _mm_setzero_si128(); + poly16 = _mm_set1_epi32(0x100B100B); // PRIM_POLY = 0x1100B + + byte_size += HASH_SIZE; + while (byte_size != 0){ + for (j = 0; j < 8; j++){ + ta = _mm_load_si128((__m128i *)data); // read 32-bytes + tb = _mm_load_si128((__m128i *)data + 1); + + // 128バイトを XOR して 16バイトにする + if ((j & 3) == 0) + temp16 = _mm_setzero_si128(); + temp16 = _mm_xor_si128(temp16, ta); + if ((j != 7) || (byte_size != 256)) // 最後の 16バイトだけ含めない + temp16 = _mm_xor_si128(temp16, tb); + + if ((j & 3) == 3){ // j = 3 or 7 + // 前回の値を 2倍して、今回の値を追加する + //temp16 = _mm_xor_si128(temp16, prev16); // 3倍する場合は、元の値も XOR すればいい + tl = _mm_setzero_si128(); + th = _mm_cmpgt_epi16(tl, prev16); // (0 > prev) ? 0xFFFF : 0x0000 + prev16 = _mm_slli_epi16(prev16, 1); // prev *= 2 + th = _mm_and_si128(th, poly16); // 0x100B or 0x0000 + prev16 = _mm_xor_si128(prev16, th); + + prev16 = _mm_xor_si128(prev16, temp16); + if ((j == 7) && (byte_size == 256)){ + if (hash != data + 16) // ハッシュ値の保存先が別なら + _mm_store_si128((__m128i *)hash, prev16); + _mm_store_si128(&tb, prev16); // 最後の 16バイトにハッシュ値を置く + } + } + + // split to high/low parts + th = _mm_packus_epi16(_mm_srli_epi16(tb, 8), _mm_srli_epi16(ta, 8)); + tl = _mm_packus_epi16(_mm_and_si128(tb, lmask), _mm_and_si128(ta, lmask)); + + // save to dest by extracting 16-bit masks + dtmp[0 + j] = _mm_movemask_epi8(th); + for (i = 1; i < 8; i++){ + th = _mm_slli_epi16(th, 1); // byte shift would be nicer, but ultimately doesn't matter here + dtmp[i*8 + j] = _mm_movemask_epi8(th); + } + dtmp[64 + j] = _mm_movemask_epi8(tl); + for (i = 1; i < 8; i++){ + tl = _mm_slli_epi16(tl, 1); + dtmp[64 + i*8 + j] = _mm_movemask_epi8(tl); + } + data += 32; + } + // we only really need to copy temp -> dest + memcpy(data - 256, dtmp, 256); + byte_size -= 256; + } +} + +// データの並びを元に戻すと同時にチェックサムを計算する +// buffer alignment must be 256, length must be (multiple of 256) - 16 +void checksum16_return256(unsigned char *data, unsigned char *hash, int byte_size) +{ + // 順番に処理する場合 +// galois_altmap256_return(data, byte_size + HASH_SIZE); +// checksum16(data, hash, byte_size); + + __declspec( align(16) ) unsigned short dtmp[128]; + int i, j; + __m128i ta, tb, lmask, th, tl, temp16, prev16, poly16; + + th = _mm_setzero_si128(); // shut up compiler warning + tl = _mm_setzero_si128(); + lmask = _mm_set1_epi16(0xff); + prev16 = _mm_setzero_si128(); + poly16 = _mm_set1_epi32(0x100B100B); // PRIM_POLY = 0x1100B + + byte_size += HASH_SIZE; + while (byte_size != 0){ + for (j = 0; j < 8; j++){ + // load in pattern: [0011223344556677] [8899AABBCCDDEEFF] + tl = _mm_insert_epi16(tl, *(int *)(data + 240), 0); + th = _mm_insert_epi16(th, *(int *)(data + 112), 0); + tl = _mm_insert_epi16(tl, *(int *)(data + 224), 1); + th = _mm_insert_epi16(th, *(int *)(data + 96), 1); + tl = _mm_insert_epi16(tl, *(int *)(data + 208), 2); + th = _mm_insert_epi16(th, *(int *)(data + 80), 2); + tl = _mm_insert_epi16(tl, *(int *)(data + 192), 3); + th = _mm_insert_epi16(th, *(int *)(data + 64), 3); + tl = _mm_insert_epi16(tl, *(int *)(data + 176), 4); + th = _mm_insert_epi16(th, *(int *)(data + 48), 4); + tl = _mm_insert_epi16(tl, *(int *)(data + 160), 5); + th = _mm_insert_epi16(th, *(int *)(data + 32), 5); + tl = _mm_insert_epi16(tl, *(int *)(data + 144), 6); + th = _mm_insert_epi16(th, *(int *)(data + 16), 6); + tl = _mm_insert_epi16(tl, *(int *)(data + 128), 7); + th = _mm_insert_epi16(th, *(int *)data, 7); + + // swizzle to [0123456789ABCDEF] [0123456789ABCDEF] + ta = _mm_packus_epi16(_mm_srli_epi16(tl, 8),_mm_srli_epi16(th, 8)); + tb = _mm_packus_epi16(_mm_and_si128(tl, lmask),_mm_and_si128(th, lmask)); + + // extract top bits + dtmp[j*16 + 7] = _mm_movemask_epi8(ta); + dtmp[j*16 + 15] = _mm_movemask_epi8(tb); + for (i = 1; i < 8; i++){ + ta = _mm_slli_epi16(ta, 1); + tb = _mm_slli_epi16(tb, 1); + dtmp[j*16 + 7-i] = _mm_movemask_epi8(ta); + dtmp[j*16 + 15-i] = _mm_movemask_epi8(tb); + } + data += 2; + } + + // 128バイトを XOR して 16バイトにする + temp16 = _mm_setzero_si128(); + for (j = 0; j < 8; j++){ + ta = _mm_load_si128((__m128i *)dtmp + j); + temp16 = _mm_xor_si128(temp16, ta); + } + // 前回の値を 2倍して、今回の値を追加する + //temp16 = _mm_xor_si128(temp16, prev16); // 3倍する場合は、元の値も XOR すればいい + tl = _mm_setzero_si128(); + th = _mm_cmpgt_epi16(tl, prev16); // (0 > prev) ? 0xFFFF : 0x0000 + prev16 = _mm_slli_epi16(prev16, 1); // prev *= 2 + th = _mm_and_si128(th, poly16); // 0x100B or 0x0000 + prev16 = _mm_xor_si128(prev16, th); + prev16 = _mm_xor_si128(prev16, temp16); + + if (byte_size != 256){ + // 次の 128バイトを XOR して 16バイトにする + temp16 = _mm_setzero_si128(); + for (j = 8; j < 16; j++){ + ta = _mm_load_si128((__m128i *)dtmp + j); + temp16 = _mm_xor_si128(temp16, ta); + } + // 前回の値を 2倍して、今回の値を追加する + //temp16 = _mm_xor_si128(temp16, prev16); // 3倍する場合は、元の値も XOR すればいい + tl = _mm_setzero_si128(); + th = _mm_cmpgt_epi16(tl, prev16); // (0 > prev) ? 0xFFFF : 0x0000 + prev16 = _mm_slli_epi16(prev16, 1); // prev *= 2 + th = _mm_and_si128(th, poly16); // 0x100B or 0x0000 + prev16 = _mm_xor_si128(prev16, th); + prev16 = _mm_xor_si128(prev16, temp16); + + } else { // 最後の 128バイトは特別 + // 次の 112バイトを XOR して 16バイトにする + temp16 = _mm_setzero_si128(); + for (j = 8; j < 15; j++){ + ta = _mm_load_si128((__m128i *)dtmp + j); + temp16 = _mm_xor_si128(temp16, ta); + } + // 前回の値を 2倍して、今回の値を追加する + //temp16 = _mm_xor_si128(temp16, prev16); // 3倍する場合は、元の値も XOR すればいい + tl = _mm_setzero_si128(); + th = _mm_cmpgt_epi16(tl, prev16); // (0 > prev) ? 0xFFFF : 0x0000 + prev16 = _mm_slli_epi16(prev16, 1); // prev *= 2 + th = _mm_and_si128(th, poly16); // 0x100B or 0x0000 + prev16 = _mm_xor_si128(prev16, th); + prev16 = _mm_xor_si128(prev16, temp16); + } + + // we only really need to copy temp -> dest + memcpy(data - 16, dtmp, 256); + data += 240; + byte_size -= 256; + } + + if (hash != data - 16) // ハッシュ値の保存先が別なら + _mm_store_si128((__m128i *)hash, prev16); +} + diff --git a/source/par2j/gf16.h b/source/par2j/gf16.h new file mode 100644 index 0000000..acd4231 --- /dev/null +++ b/source/par2j/gf16.h @@ -0,0 +1,72 @@ +#ifndef _GF16_H_ +#define _GF16_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +extern unsigned short *galois_log_table; +extern unsigned int cpu_flag; + +int galois_create_table(void); // Returns 0 on success, -1 on failure + +unsigned short galois_multiply(int x, int y); +unsigned short galois_multiply_fix(int x, int log_y); +unsigned short galois_divide(int x, int y); + +unsigned short galois_power(int x, int y); // 乗数計算用に追加 +unsigned short galois_reciprocal(int x); // 逆数計算用に追加 +void galois_free_table(void); // 解放用に追加 + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +extern int sse_unit; + +void galois_region_multiply( + unsigned short *r1, // Region to multiply + unsigned short *r2, // Products go here + unsigned int count, // Count of number in short + int factor); // Number to multiply by + +void galois_region_divide( + unsigned short *r1, // Region to divide. products go here + unsigned int count, // Count of number in short + int factor); // Number to divide by + +void galois_align_xor( + unsigned char *r1, // Region to multiply + unsigned char *r2, // Products go here + unsigned int len); // Byte length + +// 領域掛け算用の関数定義 +typedef void (* REGION_MULTIPLY) ( + unsigned char *r1, // Region to multiply + unsigned char *r2, // Products go here + unsigned int len, // Byte length + int factor); // Number to multiply by +REGION_MULTIPLY galois_align_multiply; + +// 領域並び替え用の関数定義 +typedef void (* REGION_ALTMAP) (unsigned char *data, unsigned int bsize); +REGION_ALTMAP galois_altmap_change; +REGION_ALTMAP galois_altmap_return; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define HASH_SIZE 16 +#define HASH_RANGE 128 + +void checksum16(unsigned char *data, unsigned char *hash, int byte_size); + +// 領域並び替えとチェックサム計算の関数定義 +typedef void (* region_checksum) (unsigned char *data, unsigned char *hash, int byte_size); +region_checksum checksum16_altmap; +region_checksum checksum16_return; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par2j/gf_jit.h b/source/par2j/gf_jit.h new file mode 100644 index 0000000..29d5f6c --- /dev/null +++ b/source/par2j/gf_jit.h @@ -0,0 +1,192 @@ +#ifndef __GF_JIT__ +#define __GF_JIT__ + +// from ParPar; "x86_jit.c" +#include +#include + +#ifdef _WIN64 +typedef unsigned __int64 FAST_U8; +typedef unsigned __int64 FAST_U16; +typedef unsigned __int64 FAST_U32; +#define FAST_U8_SIZE 8 +#define FAST_U16_SIZE 8 +#define FAST_U32_SIZE 8 +#else +typedef unsigned __int32 FAST_U8; +typedef unsigned __int32 FAST_U16; +typedef unsigned __int32 FAST_U32; +#define FAST_U8_SIZE 4 +#define FAST_U16_SIZE 4 +#define FAST_U32_SIZE 4 +#endif + +#define MAX_CPU 18 // Max number of threads + +unsigned char *jit_code = NULL; +int *jit_id; + +// 最初と最後に呼び出すこと (MAX_CPU スレッドまで対応できる) +static __inline int jit_alloc(void){ // 4KB should be enough (but, multiply for multi-core) + if (jit_code != NULL) + return 0; + jit_code = VirtualAlloc(NULL, 4096 * MAX_CPU, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (jit_code == NULL) + return -1; + jit_id = (int *)(jit_code + 4096 - 4 * MAX_CPU); + return 0; +} + +static __inline void jit_free(void){ + if (jit_code == NULL) + return; + VirtualFree(jit_code, 0, MEM_RELEASE); + jit_code = NULL; +} + + +// registers +#define AX 0 +#define BX 3 +#define CX 1 +#define DX 2 +#define DI 7 +#define SI 6 +#define BP 5 +#define SP 4 + +// conditional jumps +#define JE 0x4 +#define JNE 0x5 +#define JL 0xC +#define JGE 0xD +#define JLE 0xE +#define JG 0xF + + +#if defined(_WIN64) // 64-bit 版なら + #define RXX_PREFIX *((*jit_ptr)++) = 0x48; +#else + #define RXX_PREFIX +#endif + +static __inline void _jit_rex_pref(unsigned char **jit_ptr, uint8_t xreg, uint8_t xreg2){ +#ifdef _WIN64 + if (xreg > 7 || xreg2 > 7){ + *((*jit_ptr)++) = 0x40 | (xreg2 >>3) | ((xreg >>1)&4); + } +#endif +} + +static __inline void _jit_movaps_load(unsigned char **jit_ptr, uint8_t xreg, uint8_t mreg, int32_t offs){ + _jit_rex_pref(jit_ptr, xreg, 0); + xreg &= 7; + if ((offs+128) & ~0xFF){ + *(int32_t*)(*jit_ptr) = 0x80280F | (xreg <<19) | (mreg <<16); + *(int32_t*)((*jit_ptr) +3) = offs; + (*jit_ptr) += 7; + } else if (offs){ + *(int32_t*)(*jit_ptr) = 0x40280F | (xreg <<19) | (mreg <<16) | (offs <<24); + (*jit_ptr) += 4; + } else { + // can overflow, but we don't care + *(int32_t*)(*jit_ptr) = 0x280F | (xreg <<19) | (mreg <<16); + (*jit_ptr) += 3; + } +} +static __inline void _jit_movaps_store(unsigned char **jit_ptr, uint8_t mreg, int32_t offs, uint8_t xreg){ + _jit_rex_pref(jit_ptr, xreg, 0); + xreg &= 7; + if ((offs+128) & ~0xFF){ + *(int32_t*)(*jit_ptr) = 0x80290F | (xreg <<19) | (mreg <<16); + *(int32_t*)((*jit_ptr) +3) = offs; + jit_ptr += 7; + } else if (offs){ + *(int32_t*)(*jit_ptr) = 0x40290F | (xreg <<19) | (mreg <<16) | (offs <<24); + (*jit_ptr) += 4; + } else { + /* can overflow, but we don't care */ + *(int32_t*)(*jit_ptr) = 0x290F | (xreg <<19) | (mreg <<16); + (*jit_ptr) += 3; + } +} + +static __inline void _jit_push(unsigned char **jit_ptr, uint8_t reg){ + *((*jit_ptr)++) = 0x50 | reg; +} +static __inline void _jit_pop(unsigned char **jit_ptr, uint8_t reg){ + *((*jit_ptr)++) = 0x58 | reg; +} +static __inline void _jit_jcc(unsigned char **jit_ptr, char op, uint8_t* addr){ + int32_t target = (int32_t)(addr - (*jit_ptr) -2); + if((target+128) & ~0xFF) { + *((*jit_ptr)++) = 0x0F; + *((*jit_ptr)++) = 0x80 | op; + *(int32_t*)(*jit_ptr) = target -4; + (*jit_ptr) += 4; + } else { + *(int16_t*)(*jit_ptr) = 0x70 | op | ((int8_t)target << 8); + (*jit_ptr) += 2; + } +} +static __inline void _jit_cmp_r(unsigned char **jit_ptr, uint8_t reg, uint8_t reg2){ + RXX_PREFIX + *(int16_t*)(*jit_ptr) = 0xC039 | (reg2 << 11) | (reg << 8); + (*jit_ptr) += 2; +} +static __inline void _jit_add_i(unsigned char **jit_ptr, uint8_t reg, int32_t val){ + RXX_PREFIX + *(int16_t*)(*jit_ptr) = 0xC081 | (reg << 8); + (*jit_ptr) += 2; + *(int32_t*)(*jit_ptr) = val; + (*jit_ptr) += 4; +} +static __inline void _jit_sub_r(unsigned char **jit_ptr, uint8_t reg, uint8_t reg2){ + RXX_PREFIX + *(int16_t*)(*jit_ptr) = 0xC029 | (reg2 << 11) | (reg << 8); + (*jit_ptr) += 2; +} +static __inline void _jit_and_i(unsigned char **jit_ptr, uint8_t reg, int32_t val){ + RXX_PREFIX + *(int16_t*)(*jit_ptr) = 0xE081 | (reg << 11); + (*jit_ptr) += 2; + *(int32_t*)(*jit_ptr) = val; + (*jit_ptr) += 4; +} +static __inline void _jit_mov_i(unsigned char **jit_ptr, uint8_t reg, intptr_t val){ +#ifdef _WIN64 + if (val > 0x3fffffff || val < 0x40000000){ + *(int16_t*)(*jit_ptr) = 0xB848 | (reg << 8); + (*jit_ptr) += 2; + *(int64_t*)(*jit_ptr) = val; + (*jit_ptr) += 8; + } else { + *(int32_t*)(*jit_ptr) = 0xC0C748 | (reg << 16); + (*jit_ptr) += 3; + *(int32_t*)(*jit_ptr) = (int32_t)val; + (*jit_ptr) += 4; + } +#else + *((*jit_ptr)++) = 0xB8 | reg; + *(int32_t*)(*jit_ptr) = (int32_t)val; + (*jit_ptr) += 4; +#endif +} +static __inline void _jit_mov_r(unsigned char **jit_ptr, uint8_t reg, uint8_t reg2){ + RXX_PREFIX + *(int16_t*)(*jit_ptr) = 0xC089 | (reg2 << 11) | (reg << 8); + (*jit_ptr) += 2; +} +static __inline void _jit_nop(unsigned char **jit_ptr){ + *((*jit_ptr)++) = 0x90; +} +static __inline void _jit_align32(unsigned char **jit_ptr){ + while ((intptr_t)(*jit_ptr) & 0x1F){ + _jit_nop(jit_ptr); + } +} +static __inline void _jit_ret(unsigned char **jit_ptr){ + *((*jit_ptr)++) = 0xC3; +} + +#endif //__GF_JIT__ diff --git a/source/par2j/ini.c b/source/par2j/ini.c new file mode 100644 index 0000000..1f8eb93 --- /dev/null +++ b/source/par2j/ini.c @@ -0,0 +1,1768 @@ +// ini.c +// Copyright : 2022-10-12 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 "common2.h" +#include "crc.h" +#include "md5_crc.h" +#include "ini.h" + +/* +ファイル名は 2_#.bin (2+32+4 = 38文字) +「#」部分は Set ID を 16進数表記にする。 + +ファイル・フォーマット + +2: 検査結果の書式バージョン +4: 6バイト目以降からの CRC-32 チェックサム +4: .INI ファイルの CRC-32 チェックサム + +4: ブロック・サイズ (32-bit 以下) +4: 合計ファイル数 +4: リカバリ・セットのファイル数 +4: ファイル名用に確保される領域の文字数 (全てのファイル情報を書き込んだかどうかの識別) + +8: ファイル・サイズ +16: MD5 ハッシュ値 +16: MD5-16k ハッシュ値 +2: ファイル名 (UTF-8) のバイト数 (末尾の null 文字は含めない) +1~MAX_LEN *3: UTF-8 のファイル名 (末尾の null 文字は含めない) + + + +8: リカバリ・ファイルのサイズ +4: リカバリ・ファイルの作成日時 +4: リカバリ・ファイルの更新日時 +4: リカバリ・ファイル内に含まれるパケットの数 +4: リカバリ・ファイル内に含まれるパリティ・ブロックの数 +1: リカバリ・ファイルの状態 0xFF=未検査, 0=完全, その他=何らかのエラー + +2: パリティ・ブロックの番号 +8: パリティ・ブロックの開始位置 + + + +20 * ソース・ブロックの数: MD5 & CRC-32 を繰り返す + + +1 * ソース・ファイルの数: そのファイルのチェックサムが欠落してるか (0xFF=欠落) +20 * ソース・ブロックの数: MD5 & CRC-32 を繰り返す + + +8: ファイルの現在のサイズ (ソース・ファイル本来のサイズとは別) +4: ファイルの作成日時 +4: ファイルの更新日時 +4: ファイルの検査結果 0~=何ブロック目まで一致, -3=完全に一致 + + +8: ファイルの現在のサイズ +4: ファイルの作成日時 +4: ファイルの更新日時 +4: 記録したソース・ブロックの項目数 + +2: ソース・ブロックの番号 +8: ソース・ブロックの開始位置 +1: ソース・ブロックの検出状態 0=完全, 1=エラー訂正が必要, 8/9=分割されたブロック + + +.INI ファイルの内容 (UTF-16でエンコードされたテキスト) +[Set] +Creator = そのままで記録する +Comment = 浄化してから記録する +Checksum = ソース・ブロックのチェックサムの開始位置 + +[PAR] +ファイル識別番号 = リカバリ・ファイル情報の開始位置 + +[State] +ソース・ファイル番号とファイル識別番号 = ファイルの状態とハッシュ値の検査結果 + +[Damage] と [Simple] +ソース・ファイル番号とファイル識別番号 = ファイル状態とソース・ブロック情報の開始位置 +(比較対象が指定されて無いなら、ソース・ファイル番号を記録しない) + +*/ + +#define INI_VERSION 0x1270 // 検査結果の書式が決まった時のバージョン +#define REUSE_MIN 16384 // ファイル・サイズがこれより大きければ検査結果を利用する (16KB 以上 2GB 未満) +#define HEADER_SIZE 26 +#define HEADER_EACH 42 +#define PARITY_SIZE 25 +#define PARITY_EACH 10 +#define CHKSUM_EACH 20 +#define STATE_SIZE 20 +#define STATE_COUNT 5 +#define SOURCE_EACH 11 +#define MAX_EACH 512 +//#define VERBOSE 1 // 状態を冗長に出力する + +static HANDLE hIniBin = NULL; // バイナリ・データ用の検査結果ファイル +static int ini_off; + +// 検査結果をどのくらいの期間保存するか +int recent_data = 0; +/* + 0 = 検査結果の再利用機能を無効にする(読み込まないし、記録もしない) +1~7= 前回の検査結果を読み込んで、今回のを記録する。 + 指定された期間よりも経過した古い記録は削除される。 + 1= 1日, 2= 3日, 3= 1週間, 4= 半月, 5= 1ヶ月, 6= 1年, 7= 無制限 + +8 = 同じセットの記録を削除する、今回の結果は記録する。 + 他のセットは指定された期間よりも古いものだけ削除する。 ++16 = そのファイルの検査結果だけ参照・記録する +*/ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// File Time (UTC) を UNIX Time (UTC) に変換する +static unsigned int time_f_u(FILETIME *file_time) +{ + unsigned __int64 int8; + + // 1970/01/01 の File Time との差を求める + memcpy(&int8, file_time, 8); + int8 = (int8 - 116444736000000000) / 10000000; + + return (unsigned int)int8; +} + +#ifdef VERBOSE +// File Time (UTC) を日付の文字列に変換する +static void time_f_date(FILETIME *ft, char *buf) +{ + int len; + SYSTEMTIME st_utc, st; + + FileTimeToSystemTime(ft, &st_utc); + if (SystemTimeToTzSpecificLocalTime(NULL, &st_utc, &st) == 0) + memcpy(&st, &st_utc, sizeof(SYSTEMTIME)); + len = GetDateFormatA(LOCALE_USER_DEFAULT, DATE_SHORTDATE | LOCALE_USE_CP_ACP, &st, NULL, buf, 32); + if (len > 0){ + buf[len - 1] = ' '; + len = GetTimeFormatA(LOCALE_USER_DEFAULT, LOCALE_USE_CP_ACP, &st, NULL, buf + len, 32 - len); + } + if (len == 0){ + wsprintfA(buf, "%4d/%02d/%02d %2d:%02d:%02d", + st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); + } +} +#endif + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* +BASE32 でバイト配列を文字列にする +12-byte -> 96-bit -> 5-bit * 20 +15-byte -> 120-bit -> 5-bit * 24 +*/ +static void base32_encode3( + wchar_t *buf, // 最大で 21文字分必要 (末尾の null 文字含めて) + unsigned int v2, + unsigned int v1, + unsigned int v0) +{ + wchar_t base32_table[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + + buf[ 0] = base32_table[ v2 >> 31 ]; + buf[ 1] = base32_table[(v2 >> 26) & 0x1F]; + buf[ 2] = base32_table[(v2 >> 21) & 0x1F]; + buf[ 3] = base32_table[(v2 >> 16) & 0x1F]; + buf[ 4] = base32_table[(v2 >> 11) & 0x1F]; + buf[ 5] = base32_table[(v2 >> 6) & 0x1F]; + buf[ 6] = base32_table[(v2 >> 1) & 0x1F]; + buf[ 7] = base32_table[((v2 << 4) | (v1 >> 28)) & 0x1F]; + buf[ 8] = base32_table[(v1 >> 23) & 0x1F]; + buf[ 9] = base32_table[(v1 >> 18) & 0x1F]; + buf[10] = base32_table[(v1 >> 13) & 0x1F]; + buf[11] = base32_table[(v1 >> 8) & 0x1F]; + buf[12] = base32_table[(v1 >> 3) & 0x1F]; + buf[13] = base32_table[((v1 << 2) | (v0 >> 30)) & 0x1F]; + buf[14] = base32_table[(v0 >> 25) & 0x1F]; + buf[15] = base32_table[(v0 >> 20) & 0x1F]; + buf[16] = base32_table[(v0 >> 15) & 0x1F]; + buf[17] = base32_table[(v0 >> 10) & 0x1F]; + buf[18] = base32_table[(v0 >> 5) & 0x1F]; + buf[19] = base32_table[ v0 & 0x1F]; + buf[20] = 0; +} + +static void base32_encode4( + wchar_t *buf, // 最大で 25文字分必要 (末尾の null 文字含めて) + unsigned int v3, // ファイル番号は 3バイトまで + unsigned int v2, + unsigned int v1, + unsigned int v0) +{ + wchar_t base32_table[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + + v3 += 1; // num = 0 でも base32_encode3 と見分けが付くように +1 する + + buf[ 0] = base32_table[(v3 >> 19) & 0x1F]; + buf[ 1] = base32_table[(v3 >> 14) & 0x1F]; + buf[ 2] = base32_table[(v3 >> 9) & 0x1F]; + buf[ 3] = base32_table[(v3 >> 4) & 0x1F]; + buf[ 4] = base32_table[((v3 << 1) | (v2 >> 31)) & 0x1F]; + buf[ 5] = base32_table[(v2 >> 26) & 0x1F]; + buf[ 6] = base32_table[(v2 >> 21) & 0x1F]; + buf[ 7] = base32_table[(v2 >> 16) & 0x1F]; + buf[ 8] = base32_table[(v2 >> 11) & 0x1F]; + buf[ 9] = base32_table[(v2 >> 6) & 0x1F]; + buf[10] = base32_table[(v2 >> 1) & 0x1F]; + buf[11] = base32_table[((v2 << 4) | (v1 >> 28)) & 0x1F]; + buf[12] = base32_table[(v1 >> 23) & 0x1F]; + buf[13] = base32_table[(v1 >> 18) & 0x1F]; + buf[14] = base32_table[(v1 >> 13) & 0x1F]; + buf[15] = base32_table[(v1 >> 8) & 0x1F]; + buf[16] = base32_table[(v1 >> 3) & 0x1F]; + buf[17] = base32_table[((v1 << 2) | (v0 >> 30)) & 0x1F]; + buf[18] = base32_table[(v0 >> 25) & 0x1F]; + buf[19] = base32_table[(v0 >> 20) & 0x1F]; + buf[20] = base32_table[(v0 >> 15) & 0x1F]; + buf[21] = base32_table[(v0 >> 10) & 0x1F]; + buf[22] = base32_table[(v0 >> 5) & 0x1F]; + buf[23] = base32_table[ v0 & 0x1F]; + buf[24] = 0; +} + +// 圧縮する +static void compress_text(wchar_t *buf) +{ + wchar_t replace_table[] = L"!#$%&()*,-.:<>?@]^_`{|}~"; + int i, j, len; + + // 先頭 (最上位) の 0 (BASE32/64 の表記では A) を消去する + if (buf[0] == 'A'){ + len = 1; // A が何文字続くか + while (buf[len] == 'A') + len++; + // 残りを先頭に移す + for (i = len; buf[i] != 0; i++) + buf[i - len] = buf[i]; + buf[i - len] = 0; + } + + // 連続する 0 (BASE32/64 の表記では A) を一文字に置き換える + for (j = 0; buf[j] != 0; j++){ + if ((buf[j] == 'A') && (buf[j + 1] == 'A')){ + len = 2; // A が何文字続くか + while (buf[j + len] == 'A') + len++; + // 24個の置換文字で 25連続まで対応できる + buf[j] = replace_table[len - 2]; + // 残りを前に移す + for (i = j + len; buf[i] != 0; i++) + buf[i - len + 1] = buf[i]; + buf[i - len + 1] = 0; + } + } +} + +// 置換文字の番号を返す +static int replace_decode(wchar_t c) +{ + // 置換用の文字は「 "';=[\」を除外して 24個 + wchar_t replace_table[] = L"!#$%&()*,-.:<>?@]^_`{|}~"; + int i; + + for (i = 0; i < 24; i++){ + if (replace_table[i] == c) + break; + } + + return i; // 置換用の文字で無い場合は 24 が戻る +} + +// 伸張する +static void expand_text(wchar_t *buf, int orig_count) +{ + int i, j, len, cur_count; + + cur_count = (int)wcslen(buf); + + // 置換された文字を探して、元に戻す + for (j = 0; buf[j] != 0; j++){ + len = replace_decode(buf[j]) + 2; // A が何文字続いてたのか + if (len < 26){ + // 残りを後ろに移す + for (i = cur_count; i > j; i--) + buf[i + len - 1] = buf[i]; + // 連続した A に戻す + for (i = j; i < j + len; i++) + buf[i] = 'A'; + cur_count += len - 1; // 文字数を増やしておく + j += len - 1; + } + } + + if (cur_count < orig_count){ // 本来の文字数よりも短いなら + len = orig_count - cur_count; + // 残りを末尾に移す + for (i = cur_count; i >= 0; i--) + buf[i + len] = buf[i]; + // 先頭に A を追加する + for (i = 0; i < len; i++) + buf[i] = 'A'; + } +} + +// ファイルのオブジェクトID + ボリュームのシリアル番号 を文字列に整形する +static void format_id( + wchar_t *buf, + int num, + unsigned int id[3]) +{ + if (num >= 0){ // ファイル番号を指定する + base32_encode4(buf, num, id[2], id[1], id[0]); + } else { + base32_encode3(buf, id[2], id[1], id[0]); + } + compress_text(buf); +} + +/* +BASE64 でバイト配列と文字列を変換する +19-byte -> 152-bit -> 6-bit * 26 +*/ +static void base64_encode5( + wchar_t *buf, // 最大で 27文字分必要 (末尾の null 文字含めて) + unsigned int state[4], + int result) // 検査結果は 3バイトまで +{ + wchar_t base64_table[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + unsigned int v0, v1, v2, v3, v4; + + v4 = state[1]; // 64-bit 整数のファイル・サイズの上位 (0 になることが多い) + v3 = state[0]; // 64-bit 整数のファイル・サイズの下位 + v2 = state[2]; // 作成日時 + v1 = state[3] - state[2]; // 更新日時と作成日時の差分 + v0 = result + 3; // 正の整数にするため、-3 -> 0, 0~ -> 3~ になる + + // 上位に多い 0 を先頭に置くため、ファイル・サイズと日時は Big endian で記録する + buf[ 0] = base64_table[(v4 >> 26) & 0x3F]; + buf[ 1] = base64_table[(v4 >> 20) & 0x3F]; + buf[ 2] = base64_table[(v4 >> 14) & 0x3F]; + buf[ 3] = base64_table[(v4 >> 8) & 0x3F]; + buf[ 4] = base64_table[(v4 >> 2) & 0x3F]; + buf[ 5] = base64_table[((v4 << 4) | (v3 >> 28)) & 0x3F]; + buf[ 6] = base64_table[(v3 >> 22) & 0x3F]; + buf[ 7] = base64_table[(v3 >> 16) & 0x3F]; + buf[ 8] = base64_table[(v3 >> 10) & 0x3F]; + buf[ 9] = base64_table[(v3 >> 4) & 0x3F]; + buf[10] = base64_table[((v3 << 2) | (v2 >> 30)) & 0x3F]; + buf[11] = base64_table[(v2 >> 24) & 0x3F]; + buf[12] = base64_table[(v2 >> 18) & 0x3F]; + buf[13] = base64_table[(v2 >> 12) & 0x3F]; + buf[14] = base64_table[(v2 >> 6) & 0x3F]; + buf[15] = base64_table[ v2 & 0x3F]; + // 上位に多い 0 を末尾に置くため、日時の差分と v0 は Little endian で記録する + buf[16] = base64_table[ v1 & 0x3F]; + buf[17] = base64_table[(v1 >> 6) & 0x3F]; + buf[18] = base64_table[(v1 >> 12) & 0x3F]; + buf[19] = base64_table[(v1 >> 18) & 0x3F]; + buf[20] = base64_table[(v1 >> 24) & 0x3F]; + buf[21] = base64_table[((v1 >> 30) | (v0 << 2)) & 0x3F]; + buf[22] = base64_table[(v0 >> 4) & 0x3F]; + buf[23] = base64_table[(v0 >> 10) & 0x3F]; + buf[24] = base64_table[(v0 >> 16) & 0x3F]; + buf[25] = base64_table[(v0 >> 22) & 0x3F]; + buf[26] = 0; + + //printf("0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X\n", v4, v3, v2, v1, v0); + //printf("BASE64: %S\n", buf); + compress_text(buf); + //printf("Write : %S\n", buf); +} + +// BASE64 文字の番号を返す +static int base64_decode(wchar_t c) +{ + wchar_t base64_table[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i; + + for (i = 0; i < 64; i++){ + if (base64_table[i] == c) + break; + } + + return i; +} + +static unsigned int base64_decode5( + wchar_t *buf, // 最大で 27文字分必要 (末尾の null 文字含めて) + unsigned int state[4]) +{ + unsigned int v0, v1, v2, v3, v4; + + //printf("Read : %S\n", buf); + expand_text(buf, 26); + //printf("BASE64: %S\n", buf); + + // ファイル・サイズと日時を Big endian で読み取る + v4 = base64_decode(buf[ 0]) << 26; + v4 |= base64_decode(buf[ 1]) << 20; + v4 |= base64_decode(buf[ 2]) << 14; + v4 |= base64_decode(buf[ 3]) << 8; + v4 |= base64_decode(buf[ 4]) << 2; + v3 = base64_decode(buf[ 5]); + v4 |= v3 >> 4; + v3 = v3 << 28; + v3 |= base64_decode(buf[ 6]) << 22; + v3 |= base64_decode(buf[ 7]) << 16; + v3 |= base64_decode(buf[ 8]) << 10; + v3 |= base64_decode(buf[ 9]) << 4; + v2 = base64_decode(buf[10]); + v3 |= v2 >> 2; + v2 = v2 << 30; + v2 |= base64_decode(buf[11]) << 24; + v2 |= base64_decode(buf[12]) << 18; + v2 |= base64_decode(buf[13]) << 12; + v2 |= base64_decode(buf[14]) << 6; + v2 |= base64_decode(buf[15]); + // 日時の差分と v0 は Little endian で読み取る + v1 = base64_decode(buf[16]); + v1 |= base64_decode(buf[17]) << 6; + v1 |= base64_decode(buf[18]) << 12; + v1 |= base64_decode(buf[19]) << 18; + v1 |= base64_decode(buf[20]) << 24; + v0 = base64_decode(buf[21]); + v1 |= v0 << 30; + v0 = v0 >> 2; + v0 |= base64_decode(buf[22]) << 4; + v0 |= base64_decode(buf[23]) << 10; + v0 |= base64_decode(buf[24]) << 16; + v0 |= base64_decode(buf[25]) << 22; + + state[1] = v4; // 64-bit 整数のファイル・サイズの上位 + state[0] = v3; // 64-bit 整数のファイル・サイズの下位 + state[2] = v2; // 作成日時 + state[3] = v2 + v1; + //printf("0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X\n", v4, v3, v2, v1, v0); + return v0 - 3; // 正の整数にするため、-3 -> 0, 0~ -> 3~ になる +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// 検査結果ファイルを利用する為の関数 + +// リカバリ・ファイルの新規作成時に、同じ Set ID の記録があれば消去しておく +void reset_ini_file(unsigned char *set_id) +{ + int i; + wchar_t ini_name[INI_NAME_LEN + 1]; + + // 設定ファイルのパス + swprintf(ini_name, _countof(ini_name), L"2_%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X.bin", + set_id[0], set_id[1], set_id[2], set_id[3], set_id[4], set_id[5], set_id[6], set_id[7], + set_id[8], set_id[9], set_id[10], set_id[11],set_id[12], set_id[13], set_id[14], set_id[15]); + + // バイナリ・データ用の設定ファイルを消去する + wcscat(ini_path, ini_name); + DeleteFile(ini_path); // .bin ファイルを削除する + + // .INI ファイルに拡張子を変えておく + i = (int)wcslen(ini_path); + ini_path[i - 3] = 'i'; + ini_path[i - 2] = 'n'; + ini_path[i - 1] = 'i'; + DeleteFile(ini_path); // .ini ファイルを削除する +} + +// ini_path で指定されてる検査結果ファイルを削除する +static void delete_ini_file(void) +{ + int len; + + // 開いてるなら閉じる + if (hIniBin != NULL){ + CloseHandle(hIniBin); + hIniBin = NULL; + } + + DeleteFile(ini_path); // .ini ファイルを削除する + len = (int)wcslen(ini_path); + ini_path[len - 3] = 'b'; + ini_path[len - 2] = 'i'; + ini_path[len - 1] = 'n'; + DeleteFile(ini_path); // .bin ファイルを削除する + + recent_data = 0; // これ以降は検査結果は利用できない +} + +// 検査するリカバリ・ファイルが同じであれば、再検査する必要は無い +int check_ini_file(unsigned char *set_id) +{ + unsigned char set_data[HEADER_SIZE * 2]; + wchar_t path[MAX_LEN], ini_name[INI_NAME_LEN + 1]; + int i, match; + unsigned int file_time, time_now, time_limit; + HANDLE hFind; + WIN32_FIND_DATA FindData; + FILETIME ft; + + if (recent_data == 0) + return 0; + + // 設定ファイルのパス + swprintf(ini_name, _countof(ini_name), L"2_%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X.bin", + set_id[0], set_id[1], set_id[2], set_id[3], set_id[4], set_id[5], set_id[6], set_id[7], + set_id[8], set_id[9], set_id[10], set_id[11],set_id[12], set_id[13], set_id[14], set_id[15]); + + // 期限を設定する + switch (recent_data & 7){ + case 1: + time_limit = 3600 * 24; // 1 day + break; + case 2: + time_limit = 3600 * 24 * 3; // 3 days + break; + case 3: + time_limit = 3600 * 24 * 7; // 1 week + break; + case 4: + time_limit = 3600 * 24 * 15; // half month + break; + case 5: + time_limit = 3600 * 24 * 30; // 1 month + break; + case 6: + time_limit = 3600 * 24 * 365; // 1 year + break; + case 7: + time_limit = 0xFFFFFFFF; // unlimited + break; + default: // 期間が指定されてない + recent_data = 0; + return 0; + } + GetSystemTimeAsFileTime(&ft); // 現在のシステム時刻 (UTC) を取得する + time_now = time_f_u(&ft); // UNIX Time に変換する + + // 記録の日時を調べて、古いのは削除する + match = 0; + wcscpy(path, ini_path); + wcscat(path, L"2_*.bin"); // 文字数は後でチェックする + hFind = FindFirstFile(path, &FindData); + if (hFind != INVALID_HANDLE_VALUE){ + do { + if (((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) && + (wcslen(FindData.cFileName) == INI_NAME_LEN)){ + // 指定時間より古い記録は削除する + file_time = time_f_u(&(FindData.ftLastWriteTime)); // 更新日時 + if (time_now - file_time > time_limit){ + wcscpy(path, ini_path); + wcscat(path, FindData.cFileName); + DeleteFile(path); + i = (int)wcslen(path); + path[i - 3] = 'i'; + path[i - 2] = 'n'; + path[i - 1] = 'i'; + DeleteFile(path); +#if VERBOSE > 1 + time_f_date(&(FindData.ftLastWriteTime), (char *)path); + printf("Delete result: %S, %s\n", FindData.cFileName, (char *)path); +#endif + } else if (wcscmp(FindData.cFileName, ini_name) == 0){ + if (recent_data & 8){ // 検査結果を読み込まないが、今回のは書き込む場合 + // 以前の検査結果が存在すれば削除する + wcscpy(path, ini_path); + wcscat(path, ini_name); + DeleteFile(path); + i = (int)wcslen(path); + path[i - 3] = 'i'; + path[i - 2] = 'n'; + path[i - 1] = 'i'; + DeleteFile(path); +#if VERBOSE > 1 + printf("Delete result: %S\n", ini_name); +#endif + } else { + match = 1; // 設定ファイルが存在する + } +#ifdef VERBOSE + ft = FindData.ftLastWriteTime; // 検査結果の更新日時 +#endif + } + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + } + + // バイナリ・データ用の設定ファイルを開く + wcscat(ini_path, ini_name); + hIniBin = CreateFile(ini_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_FLAG_RANDOM_ACCESS, NULL); + if (hIniBin == INVALID_HANDLE_VALUE){ + hIniBin = NULL; + recent_data = 0; // 開けない場合は再利用しない + return 0; + } + // .INI ファイルに拡張子を変えておく + i = (int)wcslen(ini_path); + ini_path[i - 3] = 'i'; + ini_path[i - 2] = 'n'; + ini_path[i - 1] = 'i'; + //printf("ini file path = %S\n", ini_path); + + // 既に同名の (Set ID が同じ) ファイルが存在する場合 + set_data[0] = INI_VERSION >> 8; // 検査結果の書式バージョン + set_data[1] = INI_VERSION & 0xFF; + memset(set_data + 2, 0, 8); // CRC-32 は後で書き込む + memcpy(set_data + 10, &block_size, 4); + memcpy(set_data + 14, &file_num, 4); + memcpy(set_data + 18, &entity_num, 4); + memset(set_data + 22, 0, 4); // 検査未了の印に 0 にしておく + if (match == 1){ + i = file_read_data(hIniBin, 0, set_data + HEADER_SIZE, HEADER_SIZE); + if ((set_data[HEADER_SIZE ] != (INI_VERSION >> 8)) || + (set_data[HEADER_SIZE + 1] != (INI_VERSION & 0xFF))){ + i = 1; // 古いバージョンの検査結果は参照しない + } + if (i == 0){ // 検査結果が破損してないか確かめる + if (SetFilePointer(hIniBin, 6, 0, FILE_BEGIN) != INVALID_SET_FILE_POINTER) + time_now = file_crc_part(hIniBin); + i = memcmp(&time_now, set_data + (HEADER_SIZE + 2), 4); + if (i == 0){ // .INI ファイルの破損も確かめる + HANDLE hFile; + time_limit = 0; + hFile = CreateFile(ini_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile != INVALID_HANDLE_VALUE){ + time_limit = file_crc_part(hFile); + CloseHandle(hFile); + } else { // .INI ファイルへの読み書きができなければ再利用しない + CloseHandle(hIniBin); + hIniBin = NULL; + recent_data = 0; + return 0; + } + i = memcmp(&time_limit, set_data + (HEADER_SIZE + 6), 4); + } + } + if (i == 0) // ブロック・サイズとファイル数を比較する + i = memcmp(set_data + 10, set_data + (HEADER_SIZE + 10), 12); + if (i != 0){ // ID が同じでも Set 内容が異なる場合は削除する + DeleteFile(ini_path); + if (SetFilePointer(hIniBin, 0, 0, FILE_BEGIN) != INVALID_SET_FILE_POINTER) + SetEndOfFile(hIniBin); + match = 0; + } + } + + // 一致しなかった場合は今回のデータを記録する + if (match == 0){ + if (file_write_data(hIniBin, 0, set_data, HEADER_SIZE) != 0) + delete_ini_file(); + return 0; +#ifdef VERBOSE + } else { + time_f_date(&ft, (char *)path); + printf("Date of result: %s\n", (char *)path); +#endif + } + + return match; +} + +// 検査結果ファイルを閉じる +void close_ini_file(void) +{ + if (((recent_data & 0x0F) != 0) && (hIniBin != NULL)){ + unsigned int new_crc, old_crc; + HANDLE hFile; + + // 閉じる前に検査結果の CRC-32 を計算して記録しておく + hFile = CreateFile(ini_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile != INVALID_HANDLE_VALUE){ + new_crc = file_crc_part(hFile); + CloseHandle(hFile); + } else { + CloseHandle(hIniBin); + hIniBin = NULL; + return; + } + file_read_data(hIniBin, 6, (unsigned char *)&old_crc, 4); + if (new_crc != old_crc) // .INI ファイルが変更されていれば、それも書き込む + file_write_data(hIniBin, 6, (unsigned char *)&new_crc, 4); + FlushFileBuffers(hIniBin); + file_read_data(hIniBin, 2, (unsigned char *)&old_crc, 4); + new_crc = file_crc_part(hIniBin); + if (new_crc != old_crc) // 検査結果が同じなら更新しない + file_write_data(hIniBin, 2, (unsigned char *)&new_crc, 4); + CloseHandle(hIniBin); + hIniBin = NULL; + } +} + +void write_ini_file2(unsigned char *par_client, wchar_t *par_comment) +{ + if (recent_data == 0) + return; + + // クリエーターは ASCII 文字なので「"」で囲んで記録する + if (par_client[0] != 0){ + wchar_t uni_buf[COMMENT_LEN + 2]; + int i; + uni_buf[0] = '"'; + for (i = 0; par_client[i] != 0; i++){ + uni_buf[i + 1] = par_client[i]; + } + uni_buf[i + 1] = '"'; + uni_buf[i + 2] = 0; + WritePrivateProfileString(L"Set", L"Creator", uni_buf, ini_path); + } else { + WritePrivateProfileString(L"Set", L"Creator", NULL, ini_path); + } + + // コメントを記録する (制御文字は浄化済み) + if (par_comment[0] != 0){ + int i; + // 7-bit ASCII の範囲に収まるか調べる + for (i = 0; par_comment[i] != 0; i++){ + if (par_comment[i] & 0xFF80){ + i = -1; + break; + } + } + if (i < 0){ // UTF-16 -> 16進数に変換して記録する + wchar_t uni_buf[COMMENT_LEN * 4]; + for (i = 0; par_comment[i] != 0; i++){ + swprintf(uni_buf + (i * 4), _countof(uni_buf) - i * 4, L"%04X", (unsigned short)(par_comment[i])); + } + uni_buf[i * 4] = 0; + WritePrivateProfileString(L"Set", L"Comment", uni_buf, ini_path); + } else { // ASCII 文字を Unicode と区別するために () で囲んで記録する + wchar_t uni_buf[COMMENT_LEN + 2]; + uni_buf[0] = '('; + for (i = 0; par_comment[i] != 0; i++){ + uni_buf[i + 1] = par_comment[i]; + } + uni_buf[i + 1] = ')'; + uni_buf[i + 2] = 0; + WritePrivateProfileString(L"Set", L"Comment", uni_buf, ini_path); + } + } else { + WritePrivateProfileString(L"Set", L"Comment", NULL, ini_path); + } +} + +void write_ini_file( + file_ctx_r *files) // 各ソース・ファイルの情報 +{ + unsigned char buf[MAX_LEN * 3]; + int i, len; + + if (recent_data == 0) + return; + + for (i = 0; i < file_num; i++){ + if (files[i].name <= 0) + return; // File Description packet が欠落してる場合は記録しない + } + + // ファイル位置を HEADER_SIZE 直後にする + i = SetFilePointer(hIniBin, HEADER_SIZE, NULL, FILE_BEGIN); + if ((i == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)){ + delete_ini_file(); + return; + } + for (i = 0; i < file_num; i++){ + // ソース・ファイルの情報 + memcpy(buf , &(files[i].size), 8); + memcpy(buf + 8, files[i].hash, 32); + // ファイル名は UTF-8 に変換してから記録する + utf16_to_utf8(list_buf + files[i].name, buf + HEADER_EACH); + len = (int)strlen(buf + HEADER_EACH); + memcpy(buf + 40, &len, 2); + if (WriteFile(hIniBin, buf, HEADER_EACH + len, &len, NULL) == 0){ + delete_ini_file(); + return; + } + } + + // ファイル名領域の文字数をファイル情報を全て書き込んだ印にする + if (file_write_data(hIniBin, HEADER_SIZE - 4, (unsigned char *)(&list_max), 4) != 0) + delete_ini_file(); +} + +int read_ini_file( + wchar_t *uni_buf, + file_ctx_r *files) // 各ソース・ファイルの情報 +{ + unsigned char buf[MAX_LEN * 3]; + int i, j, len; + + // ファイル名領域の文字数を取得する + if (file_read_data(hIniBin, HEADER_SIZE - 4, (unsigned char *)(&list_max), 4) != 0){ + delete_ini_file(); + return 1; + } + if (list_max == 0) // ソース・ファイルの検査が完了してない + return 1; // 前回の検査結果はそのままで再度検査する + list_len = 1; // 先頭に不明用の null 文字を置く + list_buf = (wchar_t *)malloc(list_max * 2); + if (list_buf == NULL){ + delete_ini_file(); + return 1; + } + list_buf[0] = 0; + + // ファイル位置を HEADER_SIZE 直後にする + i = SetFilePointer(hIniBin, HEADER_SIZE, NULL, FILE_BEGIN); + if ((i == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)){ + free(list_buf); + delete_ini_file(); + return 1; + } + for (j = 0; j < file_num; j++){ + if (!ReadFile(hIniBin, buf, HEADER_EACH, &i, NULL) || (HEADER_EACH != i)){ + print_win32_err(); + free(list_buf); + delete_ini_file(); + return 1; + } + // ソース・ファイルの情報 + memcpy(&(files[j].size), buf, 8); + memcpy(files[j].hash, buf + 8, 32); + len = 0; + memcpy(&len, buf + 40, 2); + if (!ReadFile(hIniBin, buf, len, &i, NULL) || (len != i)){ + print_win32_err(); + free(list_buf); + delete_ini_file(); + return 1; + } + buf[len] = 0; // 末尾に null 文字を追加しておく + utf8_to_utf16(buf, uni_buf); + len = (int)wcslen(uni_buf); // ファイル名の文字数 + wcscpy(list_buf + list_len, uni_buf); + //printf("list_max = %d, list_len = %d, len = %d\n", list_max, list_len, len); + //printf("%S\nhash = %02X, size = %I64d\n", list_buf + list_len, files[j].hash[0], files[j].size); + files[j].name = list_len; + list_len += (len + 1); + } + + if (GetPrivateProfileString(L"Set", L"Creator", L"", uni_buf, COMMENT_LEN, ini_path) > 0) + printf("Creator : %S\n", uni_buf); // ASCII なのでそのまま表示する + + i = GetPrivateProfileString(L"Set", L"Comment", L"", uni_buf, MAX_LEN, ini_path); + if (i > 0){ + if (uni_buf[0] < '0'){ + uni_buf[i - 1] = 0; + printf("Comment : %S\n", uni_buf + 1); // ASCII なのでそのまま表示する + } else { + wchar_t tmp_buf[5]; + tmp_buf[4] = 0; + for (i = 0; uni_buf[i * 4] >= '0'; i++){ + tmp_buf[0] = uni_buf[i * 4 ]; + tmp_buf[1] = uni_buf[i * 4 + 1]; + tmp_buf[2] = uni_buf[i * 4 + 2]; + tmp_buf[3] = uni_buf[i * 4 + 3]; + uni_buf[i] = (unsigned short)wcstoul(tmp_buf, NULL, 16); + } + uni_buf[i] = 0; + if (utf16_to_cp(uni_buf, buf, cp_output)) + buf[0] = 0; + if (buf[0] != 0) + printf("Comment : %s\n", buf); + } + } + + return 0; +} + +// 検査するリカバリ・ファイルが同じであれば、再検査する必要は無い +int check_ini_recovery( + HANDLE hFile, // リカバリ・ファイルのハンドル + unsigned int meta[7]) // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 +{ + unsigned char buf[PARITY_SIZE]; + wchar_t item[32]; + BY_HANDLE_FILE_INFORMATION fi; + + // 現在のファイル属性を取得する + recent_data &= ~0x10; + memset(&fi, 0, sizeof(BY_HANDLE_FILE_INFORMATION)); + if (GetFileInformationByHandle(hFile, &fi) != 0){ + meta[0] = fi.nFileSizeLow; + meta[1] = fi.nFileSizeHigh; + if ((recent_data == 0) || ((fi.nFileSizeLow <= REUSE_MIN) && (fi.nFileSizeHigh == 0))) + return 0; // 小さなファイルはサイズだけ戻す + meta[2] = time_f_u(&(fi.ftCreationTime)); + meta[3] = time_f_u(&(fi.ftLastWriteTime)); + meta[4] = fi.dwVolumeSerialNumber; + meta[5] = fi.nFileIndexLow; + meta[6] = fi.nFileIndexHigh; + recent_data |= 0x10; // 大きなファイルだけ検査結果を参照する + } else { + return -1; // 属性の読み取りエラー + } + + // 記録されてるデータと比較する + format_id(item, -1, meta + 4); // ファイル識別番号を項目名にする + ini_off = GetPrivateProfileInt(L"PAR", item, 0, ini_path); + if (ini_off != 0){ // リカバリ・ファイルの検査記録があるなら + if (file_read_data(hIniBin, ini_off, buf, PARITY_SIZE) != 0){ + delete_ini_file(); + return 0; + } +#ifdef VERBOSE + printf("check state, offset = %d, state = %02X\n", ini_off, buf[24]); +#endif + if (memcmp(buf, meta, 16) != 0) // ファイル状態が異なる + ini_off = 0; + } + + // 一致しなかった場合は今回のデータを記録する + if (ini_off == 0){ + ini_off = GetFileSize(hIniBin, NULL); // 設定ファイルのサイズ = 末尾の offset + // 未検査なので状態は 0xFF で埋める + memcpy(buf, meta, 16); + memset(buf + 16, 0xFF, PARITY_SIZE - 16); + if (file_write_data(hIniBin, ini_off, buf, PARITY_SIZE) != 0) + delete_ini_file(); +#ifdef VERBOSE + printf("start state, offset = %d\n", ini_off); +#endif + return 0; + } + + return 1; +} + +// パリティ・ブロックの状態を書き込む +void write_ini_recovery( + int id, // パリティ・ブロックの番号 + __int64 off) +{ + unsigned char buf[PARITY_EACH]; + int i; + + if ((recent_data & 0x10) == 0) + return; // 小さなファイルは検査結果を記録しない + + memcpy(buf , &id, 2); // id は 2バイトで保存する + memcpy(buf + 2, &off, 8); + if (WriteFile(hIniBin, buf, PARITY_EACH, &i, NULL) == 0){ + delete_ini_file(); + return; + } +} + +// リカバリ・ファイルの状態を書き込む +void write_ini_recovery2( + int packet_count, // そのリカバリ・ファイル内に含まれるパケットの数 + int block_count, // そのリカバリ・ファイル内に含まれるパリティ・ブロックの数 + int bad_flag, // そのリカバリ・ファイルの状態 + unsigned int meta[7]) // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 +{ + if ((recent_data & 0x10) == 0) + return; // 小さなファイルは検査結果を記録しない + recent_data &= ~0x10; + + if (packet_count != -1){ + unsigned char buf[PARITY_SIZE]; + wchar_t item[32], uni_buf[16]; + // 見つかったパリティ・ブロックの数を書き込む + memcpy(buf , &packet_count, 4); + memcpy(buf + 4, &block_count, 4); + memcpy(buf + 8, &bad_flag, 1); + if (file_write_data(hIniBin, ini_off + 16, buf, PARITY_SIZE - 16) != 0){ + delete_ini_file(); + return; + } + format_id(item, -1, meta + 4); // ファイル識別番号を項目名にする + swprintf(uni_buf, _countof(uni_buf), L"%d", ini_off); + WritePrivateProfileString(L"PAR", item, uni_buf, ini_path); +#ifdef VERBOSE + printf("write state, offset = %d, state = %02X\n", ini_off, bad_flag); +#endif + + } else { // エラー発生なら途中までの検査結果を削除する + int i = SetFilePointer(hIniBin, ini_off, NULL, FILE_BEGIN); + if ((i == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)){ + delete_ini_file(); + return; + } + if (!SetEndOfFile(hIniBin)){ + delete_ini_file(); + return; + } +#ifdef VERBOSE + printf("clear state, offset = %d\n", ini_off); +#endif + } +} + +int read_ini_recovery( + int num, + int *packet_count, // そのリカバリ・ファイル内に含まれるパケットの数 + int *block_count, // そのリカバリ・ファイル内に含まれるパリティ・ブロックの数 + int *bad_flag, // そのリカバリ・ファイルの状態 + parity_ctx_r *p_blk) // 各パリティ・ブロックの情報 +{ + unsigned char buf[PARITY_EACH * MAX_EACH], *buf_p; + int i, j, max, left, new_count = 0; + + // 状態を読み込む + if (file_read_data(hIniBin, ini_off + 16, buf, PARITY_SIZE - 16) != 0){ + delete_ini_file(); + return -1; + } + *bad_flag = 0; + memcpy(packet_count, buf , 4); + memcpy(block_count , buf + 4, 4); + memcpy(bad_flag , buf + 8, 1); + + // パリティ・ブロック情報を読み込む + max = *block_count; + left = 0; + for (i = 0; i < max; i++){ + if (left == 0){ // データを最大 MAX_EACH 個分読み込む + left = max - i; + if (left > MAX_EACH) + left = MAX_EACH; + if (!ReadFile(hIniBin, buf, PARITY_EACH * left, &j, NULL) || ((PARITY_EACH * left) != j)){ + delete_ini_file(); + return -1; + } + buf_p = buf; + } + j = 0; + memcpy(&j, buf_p, 2); + if (p_blk[j].exist == 0){ + new_count++; // リカバリ・ファイル内の新しいパリティ・ブロックの数 + p_blk[j].exist = 1; + p_blk[j].file = num; + memcpy(&(p_blk[j].off), buf_p + 2, 8); + } + buf_p += PARITY_EACH; // 次のデータへ + left--; + } + + return new_count; +} + +// Input File Slice Checksum を書き込む +void write_ini_checksum( + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + unsigned char buf[CHKSUM_EACH * MAX_EACH]; + int i, j, k, max; + + if (recent_data == 0) + return; + + ini_off = GetPrivateProfileInt(L"Set", L"Checksum", 0, ini_path); + if (ini_off != 0) // 既にチェックサムが記録されてるなら + return; + + // チェックサムの開始位置 + ini_off = GetFileSize(hIniBin, NULL); // 設定ファイルのサイズ = 末尾の offset + // チェックサムの終了位置までファイルを拡大する + i = SetFilePointer(hIniBin, ini_off + (source_num * CHKSUM_EACH), NULL, FILE_BEGIN); + if ((i == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)){ + delete_ini_file(); + return; + } + if (SetEndOfFile(hIniBin) == 0){ + delete_ini_file(); + return; + } + + for (j = 0; j < entity_num; j++){ + if (files[j].size > 0){ + max = files[j].b_off; + // ファイル位置をソース・ファイルに合わせる + i = SetFilePointer(hIniBin, ini_off + (CHKSUM_EACH * max), NULL, FILE_BEGIN); + if ((i == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)){ + delete_ini_file(); + return; + } + max += files[j].b_num; + i = files[j].b_off; + while (i < max){ + for (k = 0; (k < MAX_EACH) && (i + k < max); k++) + memcpy(buf + (CHKSUM_EACH * k), s_blk[i + k].hash, CHKSUM_EACH); + i += k; + if (!WriteFile(hIniBin, buf, CHKSUM_EACH * k, &k, NULL)){ + delete_ini_file(); + return; + } + } + } + } + // チェックサムを正しく書き込めた場合だけ項目を作る + swprintf((wchar_t *)buf, sizeof(buf) / 2, L"%d", ini_off); + WritePrivateProfileString(L"Set", L"Checksum", (wchar_t *)buf, ini_path); +} + +// Input File Slice Checksum を読み込む +int read_ini_checksum( + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + unsigned char buf[CHKSUM_EACH * MAX_EACH], *buf_p; + int i, j, k, max, left; + + if (recent_data == 0) + return -1; + + // チェックサムが記録されてるかを確かめる + ini_off = GetPrivateProfileInt(L"Set", L"Checksum", 0, ini_path); + if (ini_off == 0) // チェックサムが記録されて無いなら + return -1; + + // 存在しないチェックサムは記録から読み込む + for (j = 0; j < entity_num; j++){ + if ((files[j].size > 0) && (files[j].state & 0x80)){ + max = files[j].b_off; + // ファイル位置をソース・ファイルに合わせる + i = SetFilePointer(hIniBin, ini_off + (CHKSUM_EACH * max), NULL, FILE_BEGIN); + if ((i == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)){ + delete_ini_file(); + return -1; + } + max += files[j].b_num; + left = 0; + for (i = files[j].b_off; i < max; i++){ + if (left == 0){ // データを MAX_EACH 個分読み込む + left = max - i; + if (left > MAX_EACH) + left = MAX_EACH; + if (!ReadFile(hIniBin, buf, CHKSUM_EACH * left, &k, NULL) || ((CHKSUM_EACH * left) != k)){ + delete_ini_file(); + return -1; + } + buf_p = buf; + } + memcpy(s_blk[i].hash, buf_p, 20); + memcpy(&(s_blk[i].crc), buf_p + 16, 4); + s_blk[i].crc ^= window_mask; // CRC の初期値と最終処理の 0xFFFFFFFF を取り除く + buf_p += CHKSUM_EACH; // 次のデータへ + left--; + } + files[j].state = 0; // そのファイルのチェックサムを全て読み込んだ + } + } + +#ifdef VERBOSE + printf("read checksum, offset = %d\n", ini_off); +#endif + return 0; +} + +// Input File Slice Checksum が不完全でも読み書きする +void update_ini_checksum( + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + unsigned char buf[CHKSUM_EACH * MAX_EACH], *buf_p, checksum_state; + int i, j, k, max, left, first_time = 0; + + if (recent_data == 0) + return; + + ini_off = GetPrivateProfileInt(L"Set", L"Checksum2", 0, ini_path); + if (ini_off == 0){ // チェックサムがまだ記録されて無いなら、記録する領域を確保する + // ファイル位置を末尾にする + i = SetFilePointer(hIniBin, 0, NULL, FILE_END); + if ((i == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)){ + delete_ini_file(); + return; + } + // チェックサムの開始位置 + ini_off = GetFileSize(hIniBin, NULL); // 設定ファイルのサイズ = 末尾の offset + // チェックサムの終了位置までファイルを拡大する + i = SetFilePointer(hIniBin, source_num * CHKSUM_EACH + entity_num, NULL, FILE_CURRENT); + if ((i == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)){ + delete_ini_file(); + return; + } + if (SetEndOfFile(hIniBin) == 0){ + delete_ini_file(); + return; + } + first_time = -1; + } + + for (j = 0; j < entity_num; j++){ + if (files[j].size > 0){ + if (first_time){ + checksum_state = 1; + } else { // チェックサム状態を読み込む + if (file_read_data(hIniBin, ini_off + j, &checksum_state, 1) != 0){ + delete_ini_file(); + return; + } + } + max = files[j].b_off; + // ファイル位置をソース・ファイルに合わせる + i = SetFilePointer(hIniBin, ini_off + (CHKSUM_EACH * max + entity_num), NULL, FILE_BEGIN); + if ((i == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)){ + delete_ini_file(); + return; + } + if ((checksum_state != 0) && ((files[j].state & 0x80) == 0)){ // 存在するチェックサムだけ記録する + max += files[j].b_num; + i = files[j].b_off; + while (i < max){ + for (k = 0; (k < MAX_EACH) && (i + k < max); k++) + memcpy(buf + (CHKSUM_EACH * k), s_blk[i + k].hash, CHKSUM_EACH); + i += k; + if (!WriteFile(hIniBin, buf, CHKSUM_EACH * k, &k, NULL)){ + delete_ini_file(); + return; + } + } + } else if ((checksum_state == 0) && ((files[j].state & 0x80) != 0)){ // 存在しないチェックサムは記録から読み込む + max += files[j].b_num; + left = 0; + for (i = files[j].b_off; i < max; i++){ + if (left == 0){ // データを MAX_EACH 個分読み込む + left = max - i; + if (left > MAX_EACH) + left = MAX_EACH; + if (!ReadFile(hIniBin, buf, CHKSUM_EACH * left, &k, NULL) || ((CHKSUM_EACH * left) != k)){ + delete_ini_file(); + return; + } + buf_p = buf; + } + memcpy(s_blk[i].hash, buf_p, 20); + memcpy(&(s_blk[i].crc), buf_p + 16, 4); + s_blk[i].crc ^= window_mask; // CRC の初期値と最終処理の 0xFFFFFFFF を取り除く + buf_p += CHKSUM_EACH; // 次のデータへ + left--; + } + files[j].state = 0; // そのファイルのチェックサムを全て読み込んだ + } + // チェックサムの状態も記録する + if ((first_time) || ((checksum_state != 0) && ((files[j].state & 0x80) == 0))){ + checksum_state = (unsigned char)(files[j].state); + if (file_write_data(hIniBin, ini_off + j, &checksum_state, 1) != 0){ + delete_ini_file(); + return; + } + } + } + } + +#ifdef VERBOSE + printf("update checksum, offset = %d\n", ini_off); +#endif + if (first_time){ // 最初にチェックサムを正しく書き込めた場合だけ項目を作る + swprintf((wchar_t *)buf, sizeof(buf) / 2, L"%d", ini_off); + WritePrivateProfileString(L"Set", L"Checksum2", (wchar_t *)buf, ini_path); + } +} + +// ソース・ファイルのハッシュ値の検査結果を記録する +void write_ini_state( + int num, // ファイル番号 + unsigned int meta[7], // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 + int result) // 検査結果 0~=何ブロック目まで一致, -3=完全に一致 +{ + wchar_t item[32], uni_buf[32]; + + if ((recent_data & 0x10) == 0) + return; // 小さなファイルは検査結果を記録しない + recent_data &= ~0x10; + + // 今回の状態を書き込む + format_id(item, num, meta + 4); // ファイル識別番号を項目名にする + base64_encode5(uni_buf, meta, result); + WritePrivateProfileString(L"State", item, uni_buf, ini_path); +#ifdef VERBOSE + printf("write state, num = %d, state = %d\n", num, result); +#endif +} + +// ソース・ファイルのハッシュ値の検査結果が記録されてるかどうか +// -1=属性の読み取りエラー, -2=記録なし, 0~=何ブロック目まで一致, -3=完全に一致 +int check_ini_state( + int num, // ファイル番号 + unsigned int meta[7], // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 + HANDLE hFile) // そのファイルのハンドル +{ + wchar_t item[32], uni_buf[32]; + int state[4], result; + BY_HANDLE_FILE_INFORMATION fi; + + // 現在のファイル属性を取得する + recent_data &= ~0x10; + memset(&fi, 0, sizeof(BY_HANDLE_FILE_INFORMATION)); + if (GetFileInformationByHandle(hFile, &fi) != 0){ + meta[0] = fi.nFileSizeLow; + meta[1] = fi.nFileSizeHigh; + if ((recent_data == 0) || ((fi.nFileSizeLow <= REUSE_MIN) && (fi.nFileSizeHigh == 0))) + return -2; // 小さなファイルはサイズだけ戻す + meta[2] = time_f_u(&(fi.ftCreationTime)); + meta[3] = time_f_u(&(fi.ftLastWriteTime)); + meta[4] = fi.dwVolumeSerialNumber; + meta[5] = fi.nFileIndexLow; + meta[6] = fi.nFileIndexHigh; + recent_data |= 0x10; // 大きなファイルだけ検査結果を参照する + } else { + return -1; // 属性の読み取りエラー、検査自体を行わない + } + + // 記録されてる状態を読み込む + format_id(item, num, meta + 4); // ファイル識別番号を項目名にする + result = GetPrivateProfileString(L"State", item, L"", uni_buf, _countof(uni_buf), ini_path); + if (result == 0) // 項目が見つからない + return -2; + result = base64_decode5(uni_buf, state); + if (memcmp(meta, state, 16) != 0) // ファイル状態が異なる + return -2; +#ifdef VERBOSE + printf("check state, num = %d, state = %d\n", num, result); +#endif + + return result; +} + +// パスで指定されたソース・ファイルが完全だと書き込む +void write_ini_complete( + int num, // ファイル番号 + wchar_t *file_path) // ソース・ファイルの絶対パス +{ + wchar_t item[32], uni_buf[32]; + unsigned int meta[7]; + HANDLE hFile; + BY_HANDLE_FILE_INFORMATION fi; + + if ((recent_data & 0x0F) == 0) + return; + + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return; + // 現在のファイル属性を取得する + memset(&fi, 0, sizeof(BY_HANDLE_FILE_INFORMATION)); + if (GetFileInformationByHandle(hFile, &fi) != 0){ + meta[0] = fi.nFileSizeLow; + meta[1] = fi.nFileSizeHigh; + if ((fi.nFileSizeHigh == 0) && (fi.nFileSizeLow <= REUSE_MIN)){ + CloseHandle(hFile); + return; // 小さなファイルは検査結果を記録しない + } + meta[2] = time_f_u(&(fi.ftCreationTime)); + meta[3] = time_f_u(&(fi.ftLastWriteTime)); + meta[4] = fi.dwVolumeSerialNumber; + meta[5] = fi.nFileIndexLow; + meta[6] = fi.nFileIndexHigh; + } else { + CloseHandle(hFile); + return; // 属性の読み取りエラー + } + CloseHandle(hFile); + + // 完全という状態を書き込む + format_id(item, num, meta + 4); // ファイル識別番号を項目名にする + base64_encode5(uni_buf, meta, -3); // 完全 = -3 + WritePrivateProfileString(L"State", item, uni_buf, ini_path); +#ifdef VERBOSE + printf("write state, num = %d, state = complete\n", num); +#endif +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 末尾が途切れた時の為、指定サイズ読み込めなかった場合でもエラーにしない +static int file_read_slice( + HANDLE hFileRead, + __int64 offset, + unsigned char *buf, + unsigned int size) +{ + unsigned int rv, rv2; + + // ファイルの位置を offsetバイト目にする + rv2 = (unsigned int)(offset >> 32); + rv = (unsigned int)offset; + rv = SetFilePointer(hFileRead, rv, &rv2, FILE_BEGIN); + if ((rv == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)){ + print_win32_err(); + return 1; + } + + // size バイトを読み込む + if (!ReadFile(hFileRead, buf, size, &rv, NULL)){ + print_win32_err(); + return 2; // 指定サイズを読み込めなくてもエラーにしない + } + if (rv < size) + memset(buf + rv, 0, size - rv); // 足りなかった分は 0 で埋める + + return 0; +} + +// スライス断片を読み取ってメモリー上に一時保管する +static void read_flake( + HANDLE hFile, // ソース・ファイルのハンドル + __int64 file_off, + 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("read flake[%d] = %d, size = %d, add ok\n", i, id, flake_size); + if (side){ // 前半に上書きコピーする + if (file_read_slice(hFile, file_off, p, flake_size) != 0) + return; // 読み込み失敗 + fc->front_size = flake_size; + } else { // 後半に上書きコピーする + if (file_read_slice(hFile, file_off, p + (block_size - flake_size), flake_size) != 0) + return; // 読み込み失敗 + 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 \n", mem_size >> 20, max_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("read flake[%d] = %d, size = %u, save ok\n", rv, id, flake_size); + p = sc->flk_buf + (sizeof(flake_ctx) + (size_t)block_size) * rv; + fc = (flake_ctx *)p; + p += sizeof(flake_ctx); + fc->id = -1; // 読み込み失敗した時のために初期化しておく + if (side){ // 前半に上書きコピーする + if (file_read_slice(hFile, file_off, p, flake_size) != 0) + return; // 読み込み失敗 + fc->front_size = flake_size; + fc->rear_size = 0; + } else { // 後半に上書きコピーする + if (file_read_slice(hFile, file_off, p + (block_size - flake_size), flake_size) != 0) + return; // 読み込み失敗 + fc->front_size = 0; + fc->rear_size = flake_size; + } + fc->id = id; +} + +// スライス単位の検査結果 +// 0~=そのファイル内に含まれるスライスの数, -1=エラー, -3=記録なし +int check_ini_verify( + wchar_t *file_name, // 表示するファイル名 + HANDLE hFile, // ファイルのハンドル + int num1, // チェックサムを比較したファイルの番号 + unsigned int meta[7], // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc) // スライス検査用の情報 +{ + unsigned char buf[SOURCE_EACH * MAX_EACH], *buf_p, err_mag; + wchar_t item[MAX_LEN]; + int i, j, max, left, num; + unsigned int err_off, flag; + unsigned int time_last; + __int64 file_off, file_size; + BY_HANDLE_FILE_INFORMATION fi; + + // 現在のファイル属性を取得する + recent_data &= ~0x10; + memset(&fi, 0, sizeof(BY_HANDLE_FILE_INFORMATION)); + if (GetFileInformationByHandle(hFile, &fi) != 0){ + meta[0] = fi.nFileSizeLow; + meta[1] = fi.nFileSizeHigh; + if ((recent_data == 0) || ((fi.nFileSizeLow <= REUSE_MIN) && (fi.nFileSizeHigh == 0))) + return -3; // 小さなファイルはサイズだけ戻す + meta[2] = time_f_u(&(fi.ftCreationTime)); + meta[3] = time_f_u(&(fi.ftLastWriteTime)); + meta[4] = fi.dwVolumeSerialNumber; + meta[5] = fi.nFileIndexLow; + meta[6] = fi.nFileIndexHigh; + recent_data |= 0x10; // 大きなファイルだけ検査結果を参照する + } else { + meta[0] = 0; + meta[1] = 0; + return -1; // 属性の読み取りエラー + } + + // ファイル番号が指定されてる場合は、番号も記録する + format_id(item, num1, meta + 4); // ファイル識別番号を項目名にする + // 簡易検査と詳細検査を区別する + if (switch_v & 4){ + ini_off = GetPrivateProfileInt(L"Align", item, 0, ini_path); + } else if (switch_v & 1){ + ini_off = GetPrivateProfileInt(L"Simple", item, 0, ini_path); + } else { + ini_off = GetPrivateProfileInt(L"Damage", item, 0, ini_path); + } + + if (ini_off != 0){ + if (file_read_data(hIniBin, ini_off, buf, STATE_SIZE) != 0){ + delete_ini_file(); + return -3; + } + memcpy(&max, buf + 16, 4); + //printf("offset = %d, count = %d\n", ini_off, max); + if (memcmp(meta, buf, 16) != 0) // ファイル状態が異なる + ini_off = 0; + } + + // 一致しなかった場合は今回のデータを記録する + if (ini_off == 0){ + ini_off = GetFileSize(hIniBin, NULL); // 設定ファイルのサイズ = 末尾の offset + memcpy(buf, meta, 16); + memset(buf + 16, 0xFF, 4); // 検査未了の印 + if (file_write_data(hIniBin, ini_off, buf, STATE_SIZE) != 0){ + delete_ini_file(); + return -3; + } +#ifdef VERBOSE + printf("start state, offset = %d, switch = %d\n", ini_off, switch_v & 7); +#endif + return -3; + } + +#ifdef VERBOSE + printf("read state, offset = %d, switch = %d, find_num = %d\n", ini_off, switch_v & 7, max); +#endif + + // 一致するなら記録されてるデータを読み込む + //printf("read_ini, %S = %d\n", item, max); + time_last = GetTickCount(); + memcpy(&file_size, meta, 8); + i = SetFilePointer(hIniBin, ini_off + STATE_SIZE, NULL, FILE_BEGIN); + if ((i == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)){ + delete_ini_file(); + return -3; + } + wcscpy(item, base_dir); // 基準ディレクトリを入れておく + left = 0; + for (i = 0; i < max; i++){ + if (left == 0){ // データを MAX_EACH 個分読み込む + left = max - i; + if (left > MAX_EACH) + left = MAX_EACH; + if (!ReadFile(hIniBin, buf, SOURCE_EACH * left, &j, NULL) || ((SOURCE_EACH * left) != j)){ + delete_ini_file(); + return -3; + } + buf_p = buf; + } + flag = 0; + memcpy(&flag, buf_p + 10, 1); // エラー訂正するかどうか + j = 0; + memcpy(&j, buf_p, 2); // ソース・ブロックの番号 + memcpy(&file_off, buf_p + 2, 8); + //printf("id = %d, off = %I64d, flag = %d\n", j, file_off, flag); + if (flag & 8){ // 分割された断片 + if ((sc->flake_count >= 0) && (s_blk[j].exist == 0)){ // 未発見のブロックなら + if (flag == 8){ // 後半の断片 + read_flake(hFile, 0, j, (int)file_off, 0, sc); + } else { // 前半の断片 + __int64 file_size; + memcpy(&file_size, meta, 8); + read_flake(hFile, file_off, j, (int)(file_size - file_off), 1, sc); + } + } + } else { + if (s_blk[j].exist == 0){ // 未発見のブロックなら + s_blk[j].exist = 2; + first_num += 1; + if ((switch_v & (16 | 4)) == 16){ // コピーする + num = s_blk[j].file; + if (open_temp_file(item, num, files, sc)) + return -1; + if (flag != 1){ // そのままコピーする + if (file_copy_data(hFile, file_off, sc->hFile_tmp, + (__int64)(j - files[num].b_off) * (__int64)block_size, s_blk[j].size)){ + printf("file_copy_data, %d\n", j); + return -1; + } + } else { // 詳細検査でエラー訂正してからコピーする + if (file_read_slice(hFile, file_off, sc->buf, s_blk[j].size) != 0){ + printf("file_read_slice, %d\n", j); + return -1; + } else { // エラー訂正するブロックは少ないのでその場で CRC を逆算する + flag = crc_reverse_zero(s_blk[j].crc, block_size - s_blk[j].size); + if (correct_error(sc->buf, s_blk[j].size, s_blk[j].hash, flag, &err_off, &err_mag) < 0){ + printf("fail to correct slice, %d\n", j); + return -1; + } else { + if (file_write_data(sc->hFile_tmp, (__int64)(j - files[num].b_off) * (__int64)block_size, sc->buf, s_blk[j].size) != 0){ + printf("file_write_data, %d\n", j); + return -1; + } + } + } + } + + // 経過表示 + 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(); + } + } + } + s_blk[j].exist |= 0x1000; // このファイル内で見つけた印 + } + buf_p += SOURCE_EACH; // 次のデータへ + left--; + } + return max; +} + +// ソース・ブロックの状態を書き込む +void write_ini_verify( + int id, // ソース・ブロックの番号 + int flag, // 1=エラー訂正が必要, 8=断片の後半, 9=断片の前半 + __int64 off) +{ + unsigned char buf[SOURCE_EACH]; + int i; + + if ((recent_data & 0x10) == 0) + return; // 小さなファイルは検査結果を記録しない + + memcpy(buf , &id, 2); + memcpy(buf + 2, &off, 8); + memcpy(buf + 10, &flag, 1); + if (WriteFile(hIniBin, buf, SOURCE_EACH, &i, NULL) == 0){ + delete_ini_file(); + return; + } +} + +void write_ini_verify2( + int num1, // チェックサムを比較したファイルの番号 + unsigned int meta[7], // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 + int max) // 記録したブロック数 +{ + if ((recent_data & 0x10) == 0) + return; // 小さなファイルは検査結果を記録しない + recent_data &= ~0x10; + + if (max >= 0){ + wchar_t item[32], uni_buf[16]; + // 見つかったソース・ブロックの数を書き込む + if (file_write_data(hIniBin, ini_off + 16, (unsigned char *)(&max), 4) != 0){ + delete_ini_file(); + return; + } + // ファイル番号が指定されてる場合は、番号も記録する + format_id(item, num1, meta + 4); // ファイル識別番号を項目名にする + swprintf(uni_buf, _countof(uni_buf), L"%d", ini_off); + // 簡易検査と詳細検査を区別する + if (switch_v & 4){ + WritePrivateProfileString(L"Align", item, uni_buf, ini_path); + } else if (switch_v & 1){ + WritePrivateProfileString(L"Simple", item, uni_buf, ini_path); + } else { + WritePrivateProfileString(L"Damage", item, uni_buf, ini_path); + } +#ifdef VERBOSE + printf("write state, offset = %d, switch = %d\n", ini_off, switch_v); +#endif + + } else { // エラー発生なら途中までの検査結果を削除する + int i = SetFilePointer(hIniBin, ini_off, NULL, FILE_BEGIN); + if ((i == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)){ + delete_ini_file(); + return; + } + if (!SetEndOfFile(hIniBin)){ + delete_ini_file(); + return; + } +#ifdef VERBOSE + printf("clear state, offset = %d, error = %d\n", ini_off, -max); +#endif + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 作業用のテンポラリ・ファイルを開く +int open_temp_file( + wchar_t *temp_path, // 作業用、基準ディレクトリが入ってる + int num, // 見つけたスライスが属するファイル番号 + file_ctx_r *files, // 各ソース・ファイルの情報 + slice_ctx *sc) +{ + if (num == sc->num) + return 0; // 既に開かれてるならそのまま + + if (sc->hFile_tmp) // 他の作業ファイルが既に開かれてるなら閉じる + CloseHandle(sc->hFile_tmp); + + // スライスが所属する作業ファイルを開く + sc->hFile_tmp = handle_temp_file(list_buf + files[num].name, temp_path); + if (sc->hFile_tmp == INVALID_HANDLE_VALUE){ + sc->hFile_tmp = NULL; + return 1; + } + sc->num = num; + + return 0; +} + diff --git a/source/par2j/ini.h b/source/par2j/ini.h new file mode 100644 index 0000000..fbe807c --- /dev/null +++ b/source/par2j/ini.h @@ -0,0 +1,101 @@ +#ifndef _INI_H_ +#define _INI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +#define INI_NAME_LEN 38 // 検査結果ファイルのファイル名の文字数 + +extern int recent_data; + +// リカバリ・ファイルの新規作成時に、同じ Set ID の記録があれば消去しておく +void reset_ini_file(unsigned char *set_id); + +// 検査するリカバリ・ファイルが同じであれば、再検査する必要は無い +int check_ini_file(unsigned char *set_id); +void close_ini_file(void); +void write_ini_file2(unsigned char *par_client, wchar_t *par_comment); +void write_ini_file(file_ctx_r *files); +int read_ini_file(wchar_t *uni_buf, file_ctx_r *files); + +int check_ini_recovery( + HANDLE hFile, // リカバリ・ファイルのハンドル + unsigned int meta[7]); // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 + +void write_ini_recovery(int id, __int64 off); + +void write_ini_recovery2( + int packet_count, // そのリカバリ・ファイル内に含まれるパケットの数 + int block_count, // そのリカバリ・ファイル内に含まれるパリティ・ブロックの数 + int bad_flag, + unsigned int meta[7]); // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 + +int read_ini_recovery( + int num, + int *packet_count, // そのリカバリ・ファイル内に含まれるパケットの数 + int *block_count, // そのリカバリ・ファイル内に含まれるパリティ・ブロックの数 + int *bad_flag, + parity_ctx_r *p_blk); // 各パリティ・ブロックの情報 + +// Input File Slice Checksum を書き込む +void write_ini_checksum( + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 + +// Input File Slice Checksum を読み込む +int read_ini_checksum( + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 + +// Input File Slice Checksum が不完全でも読み書きする +void update_ini_checksum( + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 + +void write_ini_state( + int num, // ファイル番号 + unsigned int meta[7], // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 + int result); // 検査結果 0~=何ブロック目まで一致, -3=完全に一致 + +int check_ini_state( + int num, // ファイル番号 + unsigned int meta[7], // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 + HANDLE hFile); // そのファイルのハンドル + +void write_ini_complete( + int num, // ファイル番号 + wchar_t *file_path); // ソース・ファイルの絶対パス + +int check_ini_verify( + wchar_t *file_name, // 表示するファイル名 + HANDLE hFile, // ファイルのハンドル + int num1, // チェックサムを比較したファイルの番号 + unsigned int meta[7], // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk, // 各ソース・ブロックの情報 + slice_ctx *sc); // スライス検査用の情報 + +void write_ini_verify(int id, int flag, __int64 off); + +void write_ini_verify2( + int num1, // チェックサムを比較したファイルの番号 + unsigned int meta[7], // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 + int max); // 記録したブロック数 + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 作業用のテンポラリ・ファイルを開く +int open_temp_file( + wchar_t *temp_path, // 作業用、基準ディレクトリが入ってる + int num, // 見つけたスライスが属するファイル番号 + file_ctx_r *files, // 各ソース・ファイルの情報 + slice_ctx *sc); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par2j/json.c b/source/par2j/json.c new file mode 100644 index 0000000..20dc8d0 --- /dev/null +++ b/source/par2j/json.c @@ -0,0 +1,483 @@ +// json.c +// Copyright : 2023-02-06 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" + +/* +{ +"SelectedFile":"Full path of selected recovery file", +"BaseDirectory":"Full path of base directory of source files", +"RecoveryFile":[ +"Name of recovery file1", +"Name of recovery file2", +"Name of recovery file3" +], +"SourceFile":[ +"Name of source file1", +"Name of source file2", +"Name of source file3" +], +"FoundFile":[ +"Name of found file1", +"Name of found file2" +], +"ExternalFile":[ +"Name of external file1", +"Name of external file2" +], +"DamagedFile":[ +"Name of damaged file1", +"Name of damaged file2" +], +"AppendedFile":[ +"Name of appended file1", +"Name of appended file2" +], +"MissingFile":[ +"Name of missing file1", +"Name of missing file2" +], +"MisnamedFile":{ +"Correct name of misnamed file1":"Wrong name", +"Correct name of misnamed file2":"Wrong name" +} +} +*/ +static FILE *fp_json = NULL; // JSONファイル用 + +static wchar_t *list3_buf = NULL; // 検出ファイルの名前リスト +static int list3_len, list3_max; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// JSONファイルを開く +void json_open(void) +{ + wchar_t path[MAX_LEN]; + int len; + + if (fp_json != NULL) + return; // 既に開いてる場合はそのまま + + wcscpy(path, ini_path); + wcscat(path, offset_file_name(recovery_file)); + wcscat(path, L".json"); + //printf("json_path = %ls\n", path); + + // 既に存在する場合は上書きして作り直す + fp_json = _wfopen(path, L"wt, ccs=UTF-8"); + if (fp_json == NULL) + return; + + // 基礎情報を書き込む + rewind(fp_json); // Remove BOM of UTF-8 + copy_without_prefix(path, recovery_file); + unix_directory(path); + if (fwprintf(fp_json, L"{\n\"SelectedFile\":\"%s\"", path) < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + len = copy_without_prefix(path, base_dir); + path[len - 1] = 0; // 末尾の「\」を取り除く + unix_directory(path); + if (fwprintf(fp_json, L",\n\"BaseDirectory\":\"%s\"", path) < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } +} + +// JSONファイルを閉じる +void json_close(void) +{ + if (fp_json == NULL) + return; // 既に閉じてる場合はそのまま + + fputws(L"\n}\n", fp_json); + fclose(fp_json); + fp_json = NULL; +} + +// ファイル一覧 +void json_file_list(file_ctx_r *files) +{ + wchar_t path[MAX_LEN]; + int rv, i; + + if (fp_json == NULL) + return; // 開いてない場合は何もしない + + // パケットを含む (Good or Damaged) リカバリ・ファイルのリスト + // 全くパケットを含まない (Useless) ファイルは除く + if (fputws(L",\n\"RecoveryFile\":[", fp_json) < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + i = 0; + while (i < recv2_len){ + if (compare_directory(recovery_file, recv2_buf + i) == 0){ + // 指定されたリカバリ・ファイルと同じ場所ならファイル名のみ + wcscpy(path, offset_file_name(recv2_buf + i)); + } else { + // 異なる場所(External File)ならフルパス + copy_without_prefix(path, recv2_buf + i); + unix_directory(path); + } + if (i == 0){ + rv = fwprintf(fp_json, L"\n\"%s\"", path); + } else { + rv = fwprintf(fp_json, L",\n\"%s\"", path); + } + if (rv < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + + while (recv2_buf[i] != 0) + i++; + i++; + } + if (fputws(L"\n]", fp_json) < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + + // ソース・ファイルのリスト + if (fputws(L",\n\"SourceFile\":[", fp_json) < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + for (i = 0; i < file_num; i++){ + wcscpy(path, list_buf + files[i].name); + unix_directory(path); + if (i == 0){ + rv = fwprintf(fp_json, L"\n\"%s\"", path); + } else { + rv = fwprintf(fp_json, L",\n\"%s\"", path); + } + if (rv < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + } + if (fputws(L"\n]", fp_json) < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } +} + +// ファイルの状態 +void json_file_state(file_ctx_r *files) +{ + wchar_t path[MAX_LEN], path2[MAX_LEN]; + int rv, i, flag_item; + + if (fp_json == NULL) + return; // 開いてない場合は何もしない + + // 破損ファイル + flag_item = 0; + for (i = 0; i < file_num; i++){ + if (files[i].state & 2){ + wcscpy(path, list_buf + files[i].name); + unix_directory(path); + if (flag_item == 0){ + flag_item = 1; + if (fputws(L",\n\"DamagedFile\":[", fp_json) < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + rv = fwprintf(fp_json, L"\n\"%s\"", path); + } else { + rv = fwprintf(fp_json, L",\n\"%s\"", path); + } + if (rv < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + } + } + if (flag_item != 0){ + if (fputws(L"\n]", fp_json) < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + } + + // 追加されたファイル + flag_item = 0; + for (i = 0; i < file_num; i++){ + if (files[i].state & 16){ + wcscpy(path, list_buf + files[i].name); + unix_directory(path); + if (flag_item == 0){ + flag_item = 1; + if (fputws(L",\n\"AppendedFile\":[", fp_json) < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + rv = fwprintf(fp_json, L"\n\"%s\"", path); + } else { + rv = fwprintf(fp_json, L",\n\"%s\"", path); + } + if (rv < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + } + } + if (flag_item != 0){ + if (fputws(L"\n]", fp_json) < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + } + + // 消失ファイル + flag_item = 0; + for (i = 0; i < file_num; i++){ + if (files[i].state & 1){ + wcscpy(path, list_buf + files[i].name); + unix_directory(path); + if (flag_item == 0){ + flag_item = 1; + if (fputws(L",\n\"MissingFile\":[", fp_json) < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + rv = fwprintf(fp_json, L"\n\"%s\"", path); + } else { + rv = fwprintf(fp_json, L",\n\"%s\"", path); + } + if (rv < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + } + } + if (flag_item != 0){ + if (fputws(L"\n]", fp_json) < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + } + + // 別名ファイルは、"本来の名前":"間違った名前" というセットにする + flag_item = 0; + for (i = 0; i < file_num; i++){ + if (files[i].state & 32){ + // ソースファイルの本来の名前 + wcscpy(path, list_buf + files[i].name); + unix_directory(path); + // 別名 + wcscpy(path2, list_buf + files[i].name2); + unix_directory(path2); + if (flag_item == 0){ + flag_item = 1; + if (fputws(L",\n\"MisnamedFile\":{", fp_json) < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + rv = fwprintf(fp_json, L"\n\"%s\":\"%s\"", path, path2); + } else { + rv = fwprintf(fp_json, L",\n\"%s\":\"%s\"", path, path2); + } + if (rv < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + } + } + if (flag_item != 0){ + if (fputws(L"\n}", fp_json) < 0){ + fclose(fp_json); + fp_json = NULL; + return; + } + } +} + +// 検出されたファイル名を保持する +void json_add_found(wchar_t *filename, int flag_external) +{ + wchar_t *tmp_p; + int len; + + if (fp_json == NULL) + return; // 開いてない場合は何もしない + + len = (int)wcslen(filename) + 1; + + if (list3_buf == NULL){ + list3_max = ALLOC_LEN; + list3_len = 0; + list3_buf = malloc(list3_max * 2); + if (list3_buf == NULL){ + fclose(fp_json); + fp_json = NULL; + return; + } + } else if (list3_len + len >= list3_max){ // 領域が足りなくなるなら拡張する + list3_max += ALLOC_LEN; + tmp_p = (wchar_t *)realloc(list3_buf, list3_max * 2); + if (tmp_p == NULL){ + free(list3_buf); + list3_buf = NULL; + fclose(fp_json); + fp_json = NULL; + return; + } else { + list3_buf = tmp_p; + } + } + + if (flag_external != 0){ + list3_buf[list3_len] = 'e'; + } else { + list3_buf[list3_len] = 'f'; + } + wcscpy(list3_buf + list3_len + 1, filename); + list3_len += len + 1; +} + +// 検出されたファイル名を書き込む +void json_save_found(void) +{ + wchar_t path[MAX_LEN]; + int rv, i, flag_item; + + if (fp_json == NULL) + return; // 開いてない場合は何もしない + if (list3_buf == NULL) + return; // 検出しなかった場合は何もしない + +/*{ +FILE *fp; +fp = fopen("list3.txt", "wb"); +fwrite(list3_buf, 2, list3_len, fp); +fclose(fp); +}*/ + + // ブロックを検出したファイル + flag_item = 0; + i = 0; + while (i < list3_len){ + if (list3_buf[i] == 'f'){ + wcscpy(path, list3_buf + i + 1); + unix_directory(path); + if (flag_item == 0){ + flag_item = 1; + if (fputws(L",\n\"FoundFile\":[", fp_json) < 0){ + free(list3_buf); + list3_buf = NULL; + fclose(fp_json); + fp_json = NULL; + return; + } + rv = fwprintf(fp_json, L"\n\"%s\"", path); + } else { + rv = fwprintf(fp_json, L",\n\"%s\"", path); + } + if (rv < 0){ + free(list3_buf); + list3_buf = NULL; + fclose(fp_json); + fp_json = NULL; + return; + } + } + + while (list3_buf[i] != 0) + i++; + i++; + } + if (flag_item != 0){ + if (fputws(L"\n]", fp_json) < 0){ + free(list3_buf); + list3_buf = NULL; + fclose(fp_json); + fp_json = NULL; + return; + } + } + + // ブロックを検出した外部ファイル + flag_item = 0; + i = 0; + while (i < list3_len){ + if (list3_buf[i] == 'e'){ + copy_without_prefix(path, list3_buf + i + 1); + unix_directory(path); + if (flag_item == 0){ + flag_item = 1; + if (fputws(L",\n\"ExternalFile\":[", fp_json) < 0){ + free(list3_buf); + list3_buf = NULL; + fclose(fp_json); + fp_json = NULL; + return; + } + rv = fwprintf(fp_json, L"\n\"%s\"", path); + } else { + rv = fwprintf(fp_json, L",\n\"%s\"", path); + } + if (rv < 0){ + free(list3_buf); + list3_buf = NULL; + fclose(fp_json); + fp_json = NULL; + return; + } + } + + while (list3_buf[i] != 0) + i++; + i++; + } + if (flag_item != 0){ + if (fputws(L"\n]", fp_json) < 0){ + free(list3_buf); + list3_buf = NULL; + fclose(fp_json); + fp_json = NULL; + return; + } + } + + // 最後にメモリーを開放する + free(list3_buf); + list3_buf = NULL; +} diff --git a/source/par2j/json.h b/source/par2j/json.h new file mode 100644 index 0000000..97deedc --- /dev/null +++ b/source/par2j/json.h @@ -0,0 +1,19 @@ + +// JSONファイルを開く +void json_open(void); + +// JSONファイルを閉じる +void json_close(void); + +// ファイル一覧 +void json_file_list(file_ctx_r *files); + +// ファイルの状態 +void json_file_state(file_ctx_r *files); + +// 検出されたファイル名を保持する +void json_add_found(wchar_t *filename, int flag_external); + +// 検出されたファイル名を書き込む +void json_save_found(void); + diff --git a/source/par2j/lib_opencl.c b/source/par2j/lib_opencl.c new file mode 100644 index 0000000..0a57454 --- /dev/null +++ b/source/par2j/lib_opencl.c @@ -0,0 +1,833 @@ +// lib_opencl.c +// Copyright : 2022-01-15 Yutaka Sawada +// License : GPL + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 // Windows Vista or later +#endif + +#include + +#include +#include + +#include "gf16.h" +#include "lib_opencl.h" + +//#define DEBUG_OUTPUT // 実験用 + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// 関数の定義 + +// Platform API +typedef cl_int (CL_API_CALL *API_clGetPlatformIDs)(cl_uint, cl_platform_id *, cl_uint *); +typedef cl_int (CL_API_CALL *API_clGetPlatformInfo)(cl_platform_id, cl_platform_info, size_t, void *, size_t *); + +// Device APIs +typedef cl_int (CL_API_CALL *API_clGetDeviceIDs)(cl_platform_id, cl_device_type, cl_uint, cl_device_id *, cl_uint *); +typedef cl_int (CL_API_CALL *API_clGetDeviceInfo)(cl_device_id, cl_device_info, size_t, void *, size_t *); + +// Context APIs +typedef cl_context (CL_API_CALL *API_clCreateContext)(const cl_context_properties *, cl_uint, const cl_device_id *, void (CL_CALLBACK *)(const char *, const void *, size_t, void *), void *, cl_int *); +typedef cl_int (CL_API_CALL *API_clReleaseContext)(cl_context); +typedef cl_int (CL_API_CALL *API_clGetContextInfo)(cl_context, cl_context_info, size_t, void *, size_t *); + +// Command Queue APIs +typedef cl_command_queue (CL_API_CALL *API_clCreateCommandQueue)(cl_context, cl_device_id, cl_command_queue_properties, cl_int *); +typedef cl_int (CL_API_CALL *API_clReleaseCommandQueue)(cl_command_queue); + +// Memory Object APIs +typedef cl_mem (CL_API_CALL *API_clCreateBuffer)(cl_context, cl_mem_flags, size_t, void *, cl_int *); +typedef cl_int (CL_API_CALL *API_clReleaseMemObject)(cl_mem); + +// Program Object APIs +typedef cl_program (CL_API_CALL *API_clCreateProgramWithSource)(cl_context, cl_uint, const char **, const size_t *, cl_int *); +typedef cl_int (CL_API_CALL *API_clReleaseProgram)(cl_program); +typedef cl_int (CL_API_CALL *API_clBuildProgram)(cl_program, cl_uint, const cl_device_id *,const char *, void (CL_CALLBACK *)(cl_program, void *), void *); +typedef cl_int (CL_API_CALL *API_clUnloadCompiler)(void); +typedef cl_int (CL_API_CALL *API_clUnloadPlatformCompiler)(cl_platform_id); // OpenCL 1.2 で追加された +typedef cl_int (CL_API_CALL *API_clGetProgramBuildInfo)(cl_program, cl_device_id, cl_program_build_info, size_t, void *, size_t *); + +// Kernel Object APIs +typedef cl_kernel (CL_API_CALL *API_clCreateKernel)(cl_program, const char *, cl_int *); +typedef cl_int (CL_API_CALL *API_clReleaseKernel)(cl_kernel); +typedef cl_int (CL_API_CALL *API_clSetKernelArg)(cl_kernel, cl_uint, size_t, const void *); +typedef cl_int (CL_API_CALL *API_clGetKernelWorkGroupInfo)(cl_kernel, cl_device_id, cl_kernel_work_group_info, size_t, void *, size_t *); + +// Event Object APIs +typedef cl_int (CL_API_CALL *API_clReleaseEvent)(cl_event); +typedef cl_int (CL_API_CALL *API_clGetEventInfo)(cl_event, cl_event_info, size_t, void *, size_t *); + +// Flush and Finish APIs +typedef cl_int (CL_API_CALL *API_clFlush)(cl_command_queue); +typedef cl_int (CL_API_CALL *API_clFinish)(cl_command_queue); + +// Enqueued Commands APIs +typedef cl_int (CL_API_CALL *API_clEnqueueReadBuffer)(cl_command_queue, cl_mem, cl_bool, size_t, size_t, void *, cl_uint, const cl_event *, cl_event *); +typedef cl_int (CL_API_CALL *API_clEnqueueWriteBuffer)(cl_command_queue, cl_mem, cl_bool, size_t, size_t, const void *, cl_uint, const cl_event *, cl_event *); +typedef void * (CL_API_CALL *API_clEnqueueMapBuffer)(cl_command_queue, cl_mem, cl_bool, cl_map_flags, size_t, size_t, cl_uint, const cl_event *, cl_event *, cl_int *); +typedef cl_int (CL_API_CALL *API_clEnqueueUnmapMemObject)(cl_command_queue, cl_mem, void *, cl_uint, const cl_event *, cl_event *); +typedef cl_int (CL_API_CALL *API_clEnqueueNDRangeKernel)(cl_command_queue, cl_kernel, cl_uint, const size_t *, const size_t *, const size_t *, cl_uint, const cl_event *, cl_event *); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// グローバル変数 + +extern unsigned int cpu_flag, cpu_cache; // declared in common2.h +extern int cpu_num; + +#define MAX_DEVICE 3 +#define MAX_GROUP_NUM 64 + +HMODULE hLibOpenCL = NULL; + +cl_context OpenCL_context = NULL; +cl_command_queue OpenCL_command = NULL; +cl_kernel OpenCL_kernel = NULL; +cl_mem OpenCL_src = NULL, OpenCL_dst = NULL, OpenCL_buf = NULL; +size_t OpenCL_group_num; +int OpenCL_method = 0; // 正=速い機器を選ぶ, 負=遅い機器を選ぶ + +API_clCreateBuffer gfn_clCreateBuffer; +API_clReleaseMemObject gfn_clReleaseMemObject; +API_clSetKernelArg gfn_clSetKernelArg; +API_clFinish gfn_clFinish; +API_clEnqueueReadBuffer gfn_clEnqueueReadBuffer; +API_clEnqueueWriteBuffer gfn_clEnqueueWriteBuffer; +API_clEnqueueMapBuffer gfn_clEnqueueMapBuffer; +API_clEnqueueUnmapMemObject gfn_clEnqueueUnmapMemObject; +API_clEnqueueNDRangeKernel gfn_clEnqueueNDRangeKernel; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* +入力 +OpenCL_method : どのデバイスを選ぶか +unit_size : ブロックの単位サイズ +src_max : ソース・ブロック個数 +chunk_size = 0: 標準では分割しない + +出力 +return : エラー番号 +src_max : 最大で何ブロックまでソースを読み込めるか +chunk_size : CPUスレッドの分割サイズ +OpenCL_method : 動作フラグいろいろ +*/ + +// 0=成功, 1~エラー番号 +int init_OpenCL(int unit_size, int *src_max, int *chunk_size) +{ + char buf[2048], *p_source; + int err = 0, i, j; + int gpu_power, count; + size_t data_size, alloc_max; + //FILE *fp; + HRSRC res; + HGLOBAL glob; + API_clGetPlatformIDs fn_clGetPlatformIDs; +#ifdef DEBUG_OUTPUT + API_clGetPlatformInfo fn_clGetPlatformInfo; +#endif + API_clGetDeviceIDs fn_clGetDeviceIDs; + API_clGetDeviceInfo fn_clGetDeviceInfo; + API_clCreateContext fn_clCreateContext; + API_clCreateCommandQueue fn_clCreateCommandQueue; + API_clCreateProgramWithSource fn_clCreateProgramWithSource; + API_clBuildProgram fn_clBuildProgram; + API_clUnloadCompiler fn_clUnloadCompiler; + API_clUnloadPlatformCompiler fn_clUnloadPlatformCompiler; + API_clGetProgramBuildInfo fn_clGetProgramBuildInfo; + API_clReleaseProgram fn_clReleaseProgram; + API_clCreateKernel fn_clCreateKernel; + API_clGetKernelWorkGroupInfo fn_clGetKernelWorkGroupInfo; + cl_int ret; + cl_uint num_platforms = 0, num_devices = 0, num_groups, param_value; + cl_ulong param_value8, cache_size; + cl_platform_id platform_id[MAX_DEVICE], selected_platform; // Intel, AMD, Nvidia などドライバーの提供元 + cl_device_id device_id[MAX_DEVICE], selected_device; // CPU や GPU など + cl_program program; + + // ライブラリーを読み込む + hLibOpenCL = LoadLibraryA("OpenCL.DLL"); + if (hLibOpenCL == NULL) + return 1; + + // 関数のエントリー取得 + err = 2; + fn_clGetPlatformIDs = (API_clGetPlatformIDs)GetProcAddress(hLibOpenCL, "clGetPlatformIDs"); + if (fn_clGetPlatformIDs == NULL) + return err; +#ifdef DEBUG_OUTPUT + fn_clGetPlatformInfo = (API_clGetPlatformInfo)GetProcAddress(hLibOpenCL, "clGetPlatformInfo"); + if (fn_clGetPlatformInfo == NULL) + return err; +#endif + fn_clGetDeviceIDs = (API_clGetDeviceIDs)GetProcAddress(hLibOpenCL, "clGetDeviceIDs"); + if (fn_clGetDeviceIDs == NULL) + return err; + fn_clGetDeviceInfo = (API_clGetDeviceInfo)GetProcAddress(hLibOpenCL, "clGetDeviceInfo"); + if (fn_clGetDeviceInfo == NULL) + return err; + fn_clCreateContext = (API_clCreateContext)GetProcAddress(hLibOpenCL, "clCreateContext"); + if (fn_clCreateContext == NULL) + return err; + fn_clCreateCommandQueue = (API_clCreateCommandQueue)GetProcAddress(hLibOpenCL, "clCreateCommandQueue"); + if (fn_clCreateCommandQueue == NULL) + return err; + gfn_clCreateBuffer = (API_clCreateBuffer)GetProcAddress(hLibOpenCL, "clCreateBuffer"); + if (gfn_clCreateBuffer == NULL) + return err; + gfn_clReleaseMemObject = (API_clReleaseMemObject)GetProcAddress(hLibOpenCL, "clReleaseMemObject"); + if (gfn_clReleaseMemObject == NULL) + return err; + gfn_clEnqueueReadBuffer = (API_clEnqueueReadBuffer)GetProcAddress(hLibOpenCL, "clEnqueueReadBuffer"); + if (gfn_clEnqueueReadBuffer == NULL) + return err; + gfn_clEnqueueWriteBuffer = (API_clEnqueueWriteBuffer)GetProcAddress(hLibOpenCL, "clEnqueueWriteBuffer"); + if (gfn_clEnqueueWriteBuffer == NULL) + return err; + gfn_clEnqueueMapBuffer = (API_clEnqueueMapBuffer)GetProcAddress(hLibOpenCL, "clEnqueueMapBuffer"); + if (gfn_clEnqueueMapBuffer == NULL) + return err; + gfn_clEnqueueUnmapMemObject = (API_clEnqueueUnmapMemObject)GetProcAddress(hLibOpenCL, "clEnqueueUnmapMemObject"); + if (gfn_clEnqueueUnmapMemObject == NULL) + return err; + fn_clCreateProgramWithSource = (API_clCreateProgramWithSource)GetProcAddress(hLibOpenCL, "clCreateProgramWithSource"); + if (fn_clCreateProgramWithSource == NULL) + return err; + fn_clBuildProgram = (API_clBuildProgram)GetProcAddress(hLibOpenCL, "clBuildProgram"); + if (fn_clBuildProgram == NULL) + return err; + fn_clGetProgramBuildInfo = (API_clGetProgramBuildInfo)GetProcAddress(hLibOpenCL, "clGetProgramBuildInfo"); + if (fn_clGetProgramBuildInfo == NULL) + return err; + fn_clReleaseProgram = (API_clReleaseProgram)GetProcAddress(hLibOpenCL, "clReleaseProgram"); + if (fn_clReleaseProgram == NULL) + return err; + fn_clUnloadPlatformCompiler = (API_clUnloadPlatformCompiler)GetProcAddress(hLibOpenCL, "clUnloadPlatformCompiler"); + if (fn_clUnloadPlatformCompiler == NULL){ // OpenCL 1.1 なら clUnloadCompiler を使う + fn_clUnloadCompiler = (API_clUnloadCompiler)GetProcAddress(hLibOpenCL, "clUnloadCompiler"); + if (fn_clUnloadCompiler == NULL) + return err; + } + fn_clCreateKernel = (API_clCreateKernel)GetProcAddress(hLibOpenCL, "clCreateKernel"); + if (fn_clCreateKernel == NULL) + return err; + gfn_clSetKernelArg = (API_clSetKernelArg)GetProcAddress(hLibOpenCL, "clSetKernelArg"); + if (gfn_clSetKernelArg == NULL) + return err; + fn_clGetKernelWorkGroupInfo = (API_clGetKernelWorkGroupInfo)GetProcAddress(hLibOpenCL, "clGetKernelWorkGroupInfo"); + if (fn_clGetKernelWorkGroupInfo == NULL) + return err; + gfn_clFinish = (API_clFinish)GetProcAddress(hLibOpenCL, "clFinish"); + if (gfn_clFinish == NULL) + return err; + gfn_clEnqueueNDRangeKernel = (API_clEnqueueNDRangeKernel)GetProcAddress(hLibOpenCL, "clEnqueueNDRangeKernel"); + if (gfn_clEnqueueNDRangeKernel == NULL) + return err; + + // OpenCL 環境の数 + ret = fn_clGetPlatformIDs(MAX_DEVICE, platform_id, &num_platforms); + if (ret != CL_SUCCESS) + return (ret << 8) | 10; + if (OpenCL_method >= 0){ // 選択する順序と初期値を変える + OpenCL_method = 1; + gpu_power = 0; + } else { + OpenCL_method = -1; + gpu_power = INT_MIN; + } + alloc_max = 0; + + for (i = 0; i < (int)num_platforms; i++){ +#ifdef DEBUG_OUTPUT + // 環境の情報表示 + if (fn_clGetPlatformInfo(platform_id[i], CL_PLATFORM_NAME, sizeof(buf), buf, NULL) == CL_SUCCESS) + printf("\nPlatform[%d] = %s\n", i, buf); + if (fn_clGetPlatformInfo(platform_id[i], CL_PLATFORM_VERSION, sizeof(buf), buf, NULL) == CL_SUCCESS) + printf("Platform version = %s\n", buf); +#endif + + // 環境内の OpenCL 対応機器の数 + if (fn_clGetDeviceIDs(platform_id[i], CL_DEVICE_TYPE_GPU, MAX_DEVICE, device_id, &num_devices) != CL_SUCCESS) + continue; + + for (j = 0; j < (int)num_devices; j++){ + // デバイスが利用可能か確かめる + ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_AVAILABLE, sizeof(cl_uint), ¶m_value, NULL); + if ((ret != CL_SUCCESS) || (param_value == CL_FALSE)) + continue; + ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_COMPILER_AVAILABLE, sizeof(cl_uint), ¶m_value, NULL); + if ((ret != CL_SUCCESS) || (param_value == CL_FALSE)) + continue; + +#ifdef DEBUG_OUTPUT + // 機器の情報表示 + ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_NAME, sizeof(buf), buf, NULL); + if (ret == CL_SUCCESS) + printf("\nDevice[%d] = %s\n", j, buf); + ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_VERSION, sizeof(buf), buf, NULL); + if (ret == CL_SUCCESS) + printf("Device version = %s\n", buf); + ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_LOCAL_MEM_SIZE, sizeof(cl_ulong), ¶m_value8, NULL); + if (ret == CL_SUCCESS) + printf("LOCAL_MEM_SIZE = %I64d KB\n", param_value8 >> 10); + + // 無理とは思うけど、一応チェックする +//#define CL_DEVICE_SVM_CAPABILITIES 0x1053 +//#define CL_DEVICE_SVM_COARSE_GRAIN_BUFFER (1 << 0) +//#define CL_DEVICE_SVM_FINE_GRAIN_BUFFER (1 << 1) +//#define CL_DEVICE_SVM_FINE_GRAIN_SYSTEM (1 << 2) +//#define CL_DEVICE_SVM_ATOMICS (1 << 3) +// ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_SVM_CAPABILITIES, sizeof(cl_ulong), ¶m_value8, NULL); +// if (ret == CL_INVALID_VALUE) +// printf("Shared Virtual Memory is not supported\n"); +// if (ret == CL_SUCCESS) +// printf("Shared Virtual Memory = 0x%I64X\n", param_value8); +#endif + + ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_ADDRESS_BITS, sizeof(cl_uint), ¶m_value, NULL); + if (ret != CL_SUCCESS) + continue; + ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), ¶m_value8, NULL); + if (ret != CL_SUCCESS) + continue; +#ifdef DEBUG_OUTPUT + printf("ADDRESS_BITS = %d\n", param_value); + printf("MAX_MEM_ALLOC_SIZE = %I64d MB\n", param_value8 >> 20); +#endif + if (param_value == 32){ // CL_DEVICE_ADDRESS_BITS によって確保するメモリー領域の上限を変える + if (param_value8 > 0x30000000) // 768MB までにする + param_value8 = 0x30000000; + } else { // 64-bit OS でも 2GB までにする + if (param_value8 > 0x80000000) + param_value8 = 0x80000000; + } + + ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(cl_uint), &num_groups, NULL); + if (ret != CL_SUCCESS) + continue; + ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &data_size, NULL); + if (ret != CL_SUCCESS) + continue; + ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_HOST_UNIFIED_MEMORY, sizeof(cl_uint), ¶m_value, NULL); + if (ret != CL_SUCCESS) + continue; + if (param_value != 0) + param_value = 1; + +#ifdef DEBUG_OUTPUT + printf("MAX_COMPUTE_UNITS = %d\n", num_groups); + printf("MAX_WORK_GROUP_SIZE = %d\n", data_size); + printf("HOST_UNIFIED_MEMORY = %d\n", param_value); +#endif + // MAX_COMPUTE_UNITS * MAX_WORK_GROUP_SIZE で計算力を測る、外付けGPUなら値を倍にする + count = (2 - param_value) * (int)data_size * num_groups; + count *= OpenCL_method; // 符号を変える + //printf("prev = %d, now = %d\n", gpu_power, count); + if ((count > gpu_power) && (data_size >= 256) && // 256以上ないとテーブルを作れない + (param_value8 / 8 > (cl_ulong)unit_size)){ // CL_DEVICE_MAX_MEM_ALLOC_SIZE に収まるか + gpu_power = count; + selected_device = device_id[j]; // 使うデバイスの ID + selected_platform = platform_id[i]; + OpenCL_group_num = num_groups; // ワークグループ数は COMPUTE_UNITS 数にする + if (OpenCL_group_num > MAX_GROUP_NUM) // 制限を付けてローカルメモリーの消費を抑える + OpenCL_group_num = MAX_GROUP_NUM; + alloc_max = (size_t)param_value8; + + // AMD Radeon ではメモリー領域が全体の 1/4 とは限らない + ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(cl_ulong), ¶m_value8, NULL); + if (ret == CL_SUCCESS){ +#ifdef DEBUG_OUTPUT + printf("GLOBAL_MEM_SIZE = %I64d MB\n", param_value8 >> 20); +#endif + // 領域一個あたりのサイズは全体の 1/4 までにする + param_value8 /= 4; + if ((cl_ulong)alloc_max > param_value8) + alloc_max = (size_t)param_value8; + } + + cache_size = 0; + ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, sizeof(cl_uint), &num_groups, NULL); + if (ret == CL_SUCCESS){ +#ifdef DEBUG_OUTPUT + printf("GLOBAL_MEM_CACHE_TYPE = %d\n", num_groups); +#endif + if (num_groups & 3){ // CL_READ_ONLY_CACHE or CL_READ_WRITE_CACHE + ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, sizeof(cl_ulong), &cache_size, NULL); + if (ret == CL_SUCCESS){ +#ifdef DEBUG_OUTPUT + printf("GLOBAL_MEM_CACHE_SIZE = %I64d KB\n", cache_size >> 10); +#endif + if (param_value != 0){ // 内蔵 GPU なら CPU との共有キャッシュを活用する + if (cache_size >= 1048576) // サイズが小さい場合は分割しない + cache_size |= 0x40000000; + } + } + } + } + } + } + } + + if (alloc_max == 0){ +#ifdef DEBUG_OUTPUT + printf("\nAvailable GPU device was not found.\n"); +#endif + return 3; + } + +#ifdef DEBUG_OUTPUT + // デバイスの情報表示 + ret = fn_clGetPlatformInfo(selected_platform, CL_PLATFORM_NAME, sizeof(buf), buf, NULL); + if (ret == CL_SUCCESS) + printf("\nSelected platform = %s\n", buf); + ret = fn_clGetDeviceInfo(selected_device, CL_DEVICE_NAME, sizeof(buf), buf, NULL); + if (ret == CL_SUCCESS) + printf("Selected device = %s\n", buf); +#endif + + // OpenCL 利用環境を作成する + OpenCL_context = fn_clCreateContext(NULL, 1, &selected_device, NULL, NULL, &ret); + if (ret != CL_SUCCESS) + return (ret << 8) | 11; + OpenCL_command = fn_clCreateCommandQueue(OpenCL_context, selected_device, 0, &ret); + if (ret != CL_SUCCESS) + return (ret << 8) | 12; + + // 計算方式を選択する + gpu_power = unit_size; // unit_size は MEM_UNIT の倍数になってる + if ((((cpu_flag & 0x101) == 1) || ((cpu_flag & 16) != 0)) && (sse_unit == 32)){ + OpenCL_method = 2; // SSSE3 & ALTMAP または AVX2 ならデータの並び替え対応版を使う + if (cache_size & 0x40000000){ // 内蔵 GPU でキャッシュを利用できるなら、CPUスレッドと同じにする + j = cpu_cache & 0x7FFF8000; // CPUのキャッシュ上限サイズ + count = (int)(cache_size & 0x3FFFFFFF) / 4; // ただし、認識できるサイズの 1/4 までにする + if ((j == 0) || (j > count)) + j = count; + count = 1; + while (gpu_power > j){ // 制限サイズより大きいなら + // 分割数を増やして chunk のサイズを試算してみる + count++; + gpu_power = (unit_size + count - 1) / count; + gpu_power = (gpu_power + (MEM_UNIT - 1)) & ~(MEM_UNIT - 1); // MEM_UNITの倍数にする + } + if (count > 1){ + *chunk_size = gpu_power; + OpenCL_method = 3; +#ifdef DEBUG_OUTPUT + printf("gpu cache: limit size = %d, chunk size = %d, split = %d\n", j, gpu_power, count); +#endif + } +/* + // 32バイト単位のメモリーアクセスならキャッシュする必要なし?計算速度が半減する・・・ + } else if ((cache_size & 0x3FFFFFFF) > OpenCL_group_num * 4096){ // 2KB の倍はいるかも? +#ifdef DEBUG_OUTPUT + printf("gpu: cache size = %d, read size = %d\n", cache_size & 0x3FFFFFFF, OpenCL_group_num * 2048); +#endif + OpenCL_method = 1; +*/ + } + + } else if (((cpu_flag & 128) != 0) && (sse_unit == 256)){ + OpenCL_method = 4; // JIT(SSE2) は bit ごとに上位から 16バイトずつ並ぶ + // ローカルのテーブルサイズが異なることに注意 + // XOR 方式以外は 2KB (4バイト * 256項目 * 2個) 使う + // XOR (JIT) は 64バイト (4バイト * 16項目) 使う +#ifdef DEBUG_OUTPUT +// printf("4 KB cache (16-bytes * 256 work items), use if\n"); +#endif + } else { + OpenCL_method = 0; // MMX用のコードは遅いので、キャッシュ最適化する必要が無い + } + + // work group 数が必要以上に多い場合は減らす +/* + if (OpenCL_method == 4){ + // work item 一個が 16バイトずつ計算する、256個なら work group ごとに 4KB 担当する + data_size = unit_size / 4096; + } else +*/ + if (OpenCL_method & 2){ + // work item 一個が 8バイトずつ計算する、256個なら work group ごとに 2KB 担当する + data_size = unit_size / 2048; + } else { + // work item 一個が 4バイトずつ計算する、256個なら work group ごとに 1KB 担当する + data_size = unit_size / 1024; + } + if (OpenCL_group_num > data_size){ + OpenCL_group_num = data_size; + printf("Number of work groups is reduced to %d\n", (int)OpenCL_group_num); + } + + // 最大で何ブロック分のメモリー領域を保持できるのか(ここではまだ確保しない) + // 後で実際に確保する量はこれよりも少なくなる + count = (int)(alloc_max / unit_size); // 確保できるメモリー量から逆算する + if (*src_max > count) + *src_max = count; // 指定されたソース・ブロックの個数が無理なら減らす + +#ifdef DEBUG_OUTPUT + data_size = (size_t)unit_size * count; + printf("src buf : %d KB (%d blocks), possible\n", data_size >> 10, count); +#endif + + // 出力先は1ブロック分だけあればいい + // CL_MEM_ALLOC_HOST_PTRを使えばpinned memoryになるらしい + data_size = unit_size; + OpenCL_dst = gfn_clCreateBuffer(OpenCL_context, CL_MEM_WRITE_ONLY | CL_MEM_ALLOC_HOST_PTR, data_size, NULL, &ret); + if (ret != CL_SUCCESS) + return (ret << 8) | 13; +#ifdef DEBUG_OUTPUT + printf("dst buf : %d KB (%d Bytes), OK\n", data_size >> 10, data_size); +#endif + + // factor は最大個数分 (src_max個) + data_size = sizeof(unsigned short) * (*src_max); + OpenCL_buf = gfn_clCreateBuffer(OpenCL_context, CL_MEM_READ_ONLY, data_size, NULL, &ret); + if (ret != CL_SUCCESS) + return (ret << 8) | 14; +#ifdef DEBUG_OUTPUT + printf("factor buf : %d Bytes (%d factors), OK\n", data_size, (*src_max)); +#endif + +/* + // テキスト形式の OpenCL C ソース・コードを読み込む + err = 4; + fp = fopen("source.cl", "r"); + if (fp == NULL){ + printf("cannot open source code file\n"); + return err; + } + ret = fseek(fp, 0, SEEK_END); + if (ret != 0){ + printf("cannot read source code file\n"); + fclose(fp); + return err; + } + data_size = ftell(fp); + ret = fseek(fp, 0, SEEK_SET); + if (ret != 0){ + printf("cannot read source code file\n"); + fclose(fp); + return err; + } + if (data_size > 102400){ // 100 KB まで + fclose(fp); + printf("source code file is too large\n"); + return err; + } + p_source = (char *)malloc(data_size + 1); + if (p_source == NULL){ + fclose(fp); + printf("malloc error\n"); + return err; + } + data_size = fread(p_source, 1, data_size, fp); + fclose(fp); + printf("Source code length = %d characters\n", data_size); + p_source[data_size] = 0; // 末尾を null 文字にする + + // プログラムを作成する + program = fn_clCreateProgramWithSource(OpenCL_context, 1, (char **)&p_source, NULL, &ret); + if (ret != CL_SUCCESS){ + free(p_source); + return (ret << 8) | 20; + } + free(p_source); // もうテキストは要らないので開放しておく +*/ + + // リソースから OpenCL C ソース・コードを読み込む + err = 4; + // Referred to "Embedding OpenCL Kernel Files in the Application on Windows" + res = FindResource(NULL, L"#1", L"RT_STRING"); // find the resource + if (res == NULL){ + //printf("cannot find resource\n"); + return err; + } + glob = LoadResource(NULL, res); // load the resource. + if (glob == NULL){ + //printf("cannot load resource\n"); + return err; + } + p_source = (char *)LockResource(glob); // lock the resource to get a char* + if (res == NULL){ + //printf("cannot lock resource\n"); + return err; + } + data_size = SizeofResource(NULL, res); + if (data_size == 0){ + //printf("cannot get size of resource\n"); + return err; + } + //printf("Source code length = %d characters\n", data_size); + + // プログラムを作成する + program = fn_clCreateProgramWithSource(OpenCL_context, 1, (char **)&p_source, &data_size, &ret); + if (ret != CL_SUCCESS) + return (ret << 8) | 20; + FreeResource(glob); // not required ? + + // 定数を指定する + wsprintfA(buf, "-D BLK_SIZE=%d -D CHK_SIZE=%d", unit_size / 4, gpu_power / 4); + + // 使用する OpenCL デバイス用にコンパイルする + ret = fn_clBuildProgram(program, 1, &selected_device, buf, NULL, NULL); + if (ret != CL_SUCCESS){ +#ifdef DEBUG_OUTPUT + buf[0] = 0; + printf("clBuildProgram : Failed\n"); + if (fn_clGetProgramBuildInfo(program, selected_device, CL_PROGRAM_BUILD_LOG, sizeof(buf), buf, NULL) == CL_SUCCESS) + printf("%s\n", buf); +#endif + return (ret << 8) | 21; + } + + // カーネル関数を抽出する + wsprintfA(buf, "method%d", OpenCL_method & 7); + OpenCL_kernel = fn_clCreateKernel(program, buf, &ret); + if (ret != CL_SUCCESS) + return (ret << 8) | 22; +#ifdef DEBUG_OUTPUT + printf("CreateKernel : %s\n", buf); +#endif + + // カーネルが実行できる work item 数を調べる + ret = fn_clGetKernelWorkGroupInfo(OpenCL_kernel, NULL, CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &data_size, NULL); + if ((ret == CL_SUCCESS) && (data_size < 256)){ // 最低でも 256以上は必要 +#ifdef DEBUG_OUTPUT + printf("KERNEL_WORK_GROUP_SIZE = %d\n", data_size); +#endif + return (ret << 8) | 23; + } + + // プログラムを破棄する + ret = fn_clReleaseProgram(program); + if (ret != CL_SUCCESS) + return (ret << 8) | 24; + + // これ以上コンパイルしない + if (fn_clUnloadPlatformCompiler != NULL){ // OpenCL 1.2 なら + fn_clUnloadPlatformCompiler(selected_platform); + } else { // OpenCL 1.1 なら + fn_clUnloadCompiler(); + } + + // カーネル引数を指定する + ret = gfn_clSetKernelArg(OpenCL_kernel, 1, sizeof(cl_mem), &OpenCL_dst); + if (ret != CL_SUCCESS) + return (ret << 8) | 101; + ret = gfn_clSetKernelArg(OpenCL_kernel, 2, sizeof(cl_mem), &OpenCL_buf); + if (ret != CL_SUCCESS) + return (ret << 8) | 102; + if (ret != CL_SUCCESS) + return (ret << 8) | 103; + +#ifdef DEBUG_OUTPUT + // ワークアイテム数 + printf("\nMax number of work items = %d (256 * %d)\n", OpenCL_group_num * 256, OpenCL_group_num); +#endif + + return 0; +} + +int free_OpenCL(void) +{ + API_clReleaseContext fn_clReleaseContext; + API_clReleaseCommandQueue fn_clReleaseCommandQueue; + API_clReleaseKernel fn_clReleaseKernel; + int err = 0; // 最初のエラーだけ記録する + cl_int ret; + + if (hLibOpenCL == NULL) + return 0; + + // OpenCL 関連のリソースを開放する + if (OpenCL_command != NULL){ + // 動作中なら終了するのを待つ + ret = gfn_clFinish(OpenCL_command); + if ((err == 0) && (ret != CL_SUCCESS)) + err = (ret << 8) | 1; + + if (OpenCL_buf != NULL){ + ret = gfn_clReleaseMemObject(OpenCL_buf); + if ((err == 0) && (ret != CL_SUCCESS)) + err = (ret << 8) | 10; + OpenCL_buf = NULL; + } + if (OpenCL_src != NULL){ + ret = gfn_clReleaseMemObject(OpenCL_src); + if ((err == 0) && (ret != CL_SUCCESS)) + err = (ret << 8) | 11; + OpenCL_src = NULL; + } + if (OpenCL_dst != NULL){ + ret = gfn_clReleaseMemObject(OpenCL_dst); + if ((err == 0) && (ret != CL_SUCCESS)) + err = (ret << 8) | 12; + OpenCL_dst = NULL; + } + if (OpenCL_kernel != NULL){ + fn_clReleaseKernel = (API_clReleaseKernel)GetProcAddress(hLibOpenCL, "clReleaseKernel"); + if (fn_clReleaseKernel != NULL){ + ret = fn_clReleaseKernel(OpenCL_kernel); + OpenCL_kernel = NULL; + } else { + ret = 1; + } + if ((err == 0) && (ret != CL_SUCCESS)) + err = (ret << 8) | 3; + } + fn_clReleaseCommandQueue = (API_clReleaseCommandQueue)GetProcAddress(hLibOpenCL, "clReleaseCommandQueue"); + if (fn_clReleaseCommandQueue != NULL){ + ret = fn_clReleaseCommandQueue(OpenCL_command); + OpenCL_command = NULL; + } else { + ret = 1; + } + if ((err == 0) && (ret != CL_SUCCESS)) + err = (ret << 8) | 4; + } + if (OpenCL_context != NULL){ + fn_clReleaseContext = (API_clReleaseContext)GetProcAddress(hLibOpenCL, "clReleaseContext"); + if (fn_clReleaseContext != NULL){ + ret = fn_clReleaseContext(OpenCL_context); + OpenCL_context = NULL; + } else { + ret = 1; + } + if ((err == 0) && (ret != CL_SUCCESS)) + err = (ret << 8) | 5; + } + + FreeLibrary(hLibOpenCL); + hLibOpenCL = NULL; + + return err; +} + +void info_OpenCL(char *buf, int buf_size) +{ + API_clGetContextInfo fn_clGetContextInfo; + API_clGetDeviceInfo fn_clGetDeviceInfo; + cl_int ret; + + if ((hLibOpenCL == NULL) || (OpenCL_context == NULL)) + return; + + fn_clGetContextInfo = (API_clGetContextInfo)GetProcAddress(hLibOpenCL, "clGetContextInfo"); + fn_clGetDeviceInfo = (API_clGetDeviceInfo)GetProcAddress(hLibOpenCL, "clGetDeviceInfo"); + if ((fn_clGetContextInfo != NULL) && (fn_clGetDeviceInfo != NULL)){ + cl_device_id device_id; + + ret = fn_clGetContextInfo(OpenCL_context, CL_CONTEXT_DEVICES, sizeof(cl_device_id), &device_id, NULL); + if (ret == CL_SUCCESS){ + ret = fn_clGetDeviceInfo(device_id, CL_DEVICE_NAME, buf_size / 2, buf, NULL); + if (ret == CL_SUCCESS){ + ret = fn_clGetDeviceInfo(device_id, CL_DEVICE_VERSION, buf_size / 2, buf + buf_size / 2, NULL); + if (ret != CL_SUCCESS) + buf[buf_size / 2] = 0; + printf("\nOpenCL : %s, %s, 256*%d\n", buf, buf + buf_size / 2, (int)OpenCL_group_num); + } + } + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ソース・ブロックをデバイス側にコピーする +int gpu_copy_blocks( + unsigned char *data, // ブロックのバッファー (境界は 4096にすること) + int unit_size, // 4096の倍数にすること + int src_num) // 何ブロックをコピーするのか +{ + size_t data_size; + cl_int ret; + + // Integrated GPU と Discrete GPU の違いに関係なく、使う分だけ毎回メモリー領域を確保する + data_size = (size_t)unit_size * src_num; + // Intel GPUならZeroCopyできる、GeForce GPUでもメモリー消費量が少なくてコピーが速い + OpenCL_src = gfn_clCreateBuffer(OpenCL_context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, data_size, data, &ret); + if (ret != CL_SUCCESS) + return (ret << 8) | 1; +#ifdef DEBUG_OUTPUT + //printf("refer buf : %d KB (%d blocks), OK\n", data_size >> 10, src_num); +#endif + + // メモリー領域を指定する + ret = gfn_clSetKernelArg(OpenCL_kernel, 0, sizeof(cl_mem), &OpenCL_src); + if (ret != CL_SUCCESS) + return (ret << 8) | 100; + + return 0; +} + +// ソース・ブロックを掛け算する +int gpu_multiply_blocks( + int src_num, // Number of multiplying source blocks + unsigned short *mat, // Matrix of numbers to multiply by + unsigned char *buf, // Products go here + int len) // Byte length +{ + unsigned int *vram, *src, *dst; + size_t global_size, local_size; + cl_int ret; + + // 倍率の配列をデバイス側に書き込む + ret = gfn_clEnqueueWriteBuffer(OpenCL_command, OpenCL_buf, CL_FALSE, 0, sizeof(short) * src_num, mat, 0, NULL, NULL); + if (ret != CL_SUCCESS) + return (ret << 8) | 10; + + // 引数を指定する + ret = gfn_clSetKernelArg(OpenCL_kernel, 3, sizeof(int), &src_num); + if (ret != CL_SUCCESS) + return (ret << 8) | 103; + + // カーネル並列実行 + local_size = 256; // テーブルやキャッシュのため、work item 数は 256に固定する + global_size = OpenCL_group_num * 256; + //printf("group num = %d, global size = %d, local size = 256 \n", OpenCL_group_num, global_size); + ret = gfn_clEnqueueNDRangeKernel(OpenCL_command, OpenCL_kernel, 1, NULL, &global_size, &local_size, 0, NULL, NULL); + if (ret != CL_SUCCESS) + return (ret << 8) | 11; + + // 出力内容をホスト側に反映させる + vram = gfn_clEnqueueMapBuffer(OpenCL_command, OpenCL_dst, CL_TRUE, CL_MAP_READ, 0, len, 0, NULL, NULL, &ret); + if (ret != CL_SUCCESS) + return (ret << 8) | 12; + + // 4バイトごとに XOR する (SSE2 で XOR しても速くならず) + src = vram; + dst = (unsigned int *)buf; + while (len > 0){ + *dst ^= *src; + dst++; + src++; + len -= 4; + } + + // ホスト側でデータを変更しなくても、clEnqueueMapBufferと対で呼び出さないといけない + ret = gfn_clEnqueueUnmapMemObject(OpenCL_command, OpenCL_dst, vram, 0, NULL, NULL); + if (ret != CL_SUCCESS) + return (ret << 8) | 13; + + return 0; +} + +// 確保したVRAMとメモリーを解放する +int gpu_finish(void) +{ + cl_int ret; + + // 全ての処理が終わるのを待つ + ret = gfn_clFinish(OpenCL_command); + if (ret != CL_SUCCESS) + return (ret << 8) | 20; + + if (OpenCL_src != NULL){ // 確保されてる場合は解除する + ret = gfn_clReleaseMemObject(OpenCL_src); + if (ret != CL_SUCCESS) + return (ret << 8) | 21; + OpenCL_src = NULL; + } + + return 0; +} + diff --git a/source/par2j/lib_opencl.h b/source/par2j/lib_opencl.h new file mode 100644 index 0000000..dd121c7 --- /dev/null +++ b/source/par2j/lib_opencl.h @@ -0,0 +1,34 @@ +#ifndef _OPENCL_H_ +#define _OPENCL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// IntelやAMDのGPUでZeroCopyするにはメモリー境界をこの値にしないといけない +#define MEM_UNIT 4096 + +extern int OpenCL_method; + +int init_OpenCL(int unit_size, int *src_max, int *chunk_size); +int free_OpenCL(void); +void info_OpenCL(char *buf, int buf_size); + +int gpu_copy_blocks( + unsigned char *data, + int unit_size, + int src_end); + +int gpu_multiply_blocks( + int src_num, // Number of multiplying source blocks + unsigned short *mat, // Matrix of numbers to multiply by + unsigned char *buf, // Products go here + int len); // Byte length + +int gpu_finish(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par2j/list.c b/source/par2j/list.c new file mode 100644 index 0000000..e615d85 --- /dev/null +++ b/source/par2j/list.c @@ -0,0 +1,1241 @@ +// list.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 "common2.h" +#include "md5_crc.h" +#include "verify.h" +#include "ini.h" +#include "json.h" +#include "list.h" + +//#define TIMER // 実験用 + +// recovery set のファイルのハッシュ値を調べる (空のファイルは除く) +// 0x00 = ファイルが存在して完全である +// 0x01 = ファイルが存在しない +// 0x02 = ファイルが破損してる +// 0x10 = ファイルに追加されてる +static int check_file_hash( + char *ascii_buf, // ファイル名が入ってる + wchar_t *file_path, // 作業用 + int num, // file_ctx におけるファイル番号 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + int i, bad_flag = 0; + unsigned int meta_data[7]; + __int64 file_size; // 存在するファイルのサイズは本来のサイズとは異なることもある + HANDLE hFile; + + // ファイルが存在するか + wcscpy(file_path, base_dir); + wcscpy(file_path + base_len, list_buf + files[num].name); +/* +// ディスク・キャッシュを使わない場合の実験用 +i = file_hash_direct(num, file_path, list_buf + files[num].name, files, s_blk); +printf("file_hash_direct = %d\n", i); +if (i == -2) + return 2; // 検査中にキャンセルされた +*/ + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL); + if (hFile == INVALID_HANDLE_VALUE){ // ファイルが存在しない + bad_flag = 1; + } else { // ファイルの状態が記録されてるか調べる + i = check_ini_state(num, meta_data, hFile); + if (i == -1){ // エラー + CloseHandle(hFile); + bad_flag = 1; // 属性を取得できないファイルは消失と判定する + } + } + if (bad_flag == 0){ // ファイルが存在するなら状態を確認する + memcpy(&file_size, meta_data, 8); + if (file_size >= files[num].size){ // 本来のサイズまでのハッシュ値が一致するか確かめる + if (i == -2){ // 検査結果の記録が無ければ + i = file_hash_check(num, list_buf + files[num].name, hFile, 0, files, s_blk); + if (i == -2) + return 2; // 検査中にキャンセルされた + // MD5-16k が一致した時だけ検査結果を記録する + if (i != -1) + write_ini_state(num, meta_data, i); + } + if (i != -3){ // ファイルのハッシュ値が異なる + bad_flag = 2; // IO エラーでも破損扱いにして処理を続行する + if (i > 0) // 先頭に完全なブロックが見つかってるなら + bad_flag |= i << 8; // 上位 24-bit に完全なブロックの個数を記録する + // MD5-16k だけ異なるのは検出ブロック 0個として扱う + } else if (file_size > files[num].size){ // 本来のサイズまでのハッシュ値は同じ + bad_flag = 16; // 末尾にゴミが付いてないか調べる + } + } else { // file_size < files[num].size + bad_flag = 2; + } + CloseHandle(hFile); + } + + files[num].state = bad_flag; + if (bad_flag == 1){ // 消失 + printf(" - Missing : \"%s\"\n", ascii_buf); + } else { // 完全、破損または追加 (追加は特殊な破損状態) + int b_last; + if (bad_flag & 2){ // 破損なら上位 24-bit に完全なブロックの個数が記録されてる + bad_flag >>= 8; + b_last = files[num].b_off + bad_flag; // 途中まで有効と見なす + for (i = files[num].b_off; i < b_last; i++) + s_blk[i].exist = 2; + first_num += bad_flag; // ブロック数を集計する + print_progress_file(-1, first_num, NULL); // 重複を除外した合計ブロック数を表示する + printf("%13I64d %8d : \"%s\"\n", file_size, bad_flag, ascii_buf); + } else { // 完全か追加 + b_last = files[num].b_off + files[num].b_num; + for (i = files[num].b_off; i < b_last; i++) + s_blk[i].exist = 1; // 全ブロックが有効と見なす + first_num += files[num].b_num; // ブロック数を集計する + print_progress_file(-1, first_num, NULL); // 重複を除外した合計ブロック数を表示する + if (bad_flag == 0){ // サイズが同じでハッシュ値が一致したら完全 + printf(" = Complete : \"%s\"\n", ascii_buf); + } else { // 本来のサイズまでハッシュ値が一致したら追加 + printf("%13I64d Appended : \"%s\"\n", file_size, ascii_buf); + } + } + } + + return 0; +} + +// ブロックのチェックサムが欠落してるファイルを検査する +// 0x80 = ファイルが存在して完全である (全てのブロックが完全とみなす) +// 0x81 = ファイルが存在しない +// 0x82 = ファイルが破損してる +// 0x90 = ファイルに追加されてる (全てのブロックが完全とみなす) +int check_file_hash_incomplete( + char *ascii_buf, // ファイル名が入ってる + wchar_t *file_path, // 作業用 + int num, // file_ctx におけるファイル番号 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + int i, bad_flag = 0; + unsigned int meta_data[7]; + __int64 file_size; // 存在するファイルのサイズは本来のサイズとは異なることもある + HANDLE hFile; + + // ファイルが存在するか + wcscpy(file_path, base_dir); + wcscpy(file_path + base_len, list_buf + files[num].name); + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL); + if (hFile == INVALID_HANDLE_VALUE){ // ファイルが存在しない + bad_flag = 1; + } else { // ファイルの状態が記録されてるか調べる + i = check_ini_state(num, meta_data, hFile); + if (i == -1){ // エラー + CloseHandle(hFile); + bad_flag = 1; // 属性を取得できないファイルは消失と判定する + } + } + if (bad_flag == 0){ // ファイルが存在するなら状態を確認する + memcpy(&file_size, meta_data, 8); + if (file_size >= files[num].size){ // 本来のサイズまでのハッシュ値が一致するか確かめる + if (i == -2){ // 検査結果の記録が無ければ + i = file_hash_check(num, list_buf + files[num].name, hFile, INT_MAX, files, NULL); + if (i == -2) + return 2; // 検査中にキャンセルされた + // MD5-16k が一致した時だけ検査結果を記録する + if (i != -1) + write_ini_state(num, meta_data, i); + } + if (i != -3){ // ファイルのハッシュ値が異なる + bad_flag = 2; // IO エラーでも破損扱いにして処理を続行する + } else if (file_size > files[num].size){ // 本来のサイズまでのハッシュ値は同じ + bad_flag = 16; // 末尾にゴミが付いてないか調べる + } + } else { // file_size < files[num].size + bad_flag = 2; + } + CloseHandle(hFile); + } + + files[num].state = bad_flag | 0x80; // チェックサムが存在しない印 + if (bad_flag == 1){ // 消失 + printf(" - Missing : \"%s\"\n", ascii_buf); + } else { // 完全、破損または追加 (追加は特殊な破損状態) + if (bad_flag & 2){ // ブロック単位の検査ができないので全て破損として扱う + printf("%13I64d Damaged : \"%s\"\n", file_size, ascii_buf); + } else { // 完全か追加 + // ブロック単位の検査はできないがとりあえず有効として扱う + int b_last; + b_last = files[num].b_off + files[num].b_num; + for (i = files[num].b_off; i < b_last; i++) + s_blk[i].exist = 1; // 全ブロックが有効と見なす + first_num += files[num].b_num; // ブロック数を集計する + print_progress_file(-1, first_num, NULL); // 重複を除外した合計ブロック数を表示する + if (bad_flag == 0){ + printf(" = Complete : \"%s\"\n", ascii_buf); + } else { + printf("%13I64d Appended : \"%s\"\n", file_size, ascii_buf); + } + } + } + + return 0; +} + +// 空ファイルやフォルダが存在するか調べる +// non-recovery set のファイルが存在して破損してないかも調べる +// 0 = ファイルが存在して完全である +// 1 = ファイルが存在しない +// 2 = ファイルが破損してる +// 16 = ファイルに追加されてる +// 64 = フォルダが存在する +// 65 = フォルダが存在しない +static int check_file_exist( + char *ascii_buf, // ファイル名が入ってる + wchar_t *file_path, // 作業用 + int num, // file_ctx におけるファイル番号 + file_ctx_r *files) // 各ソース・ファイルの情報 +{ + int i, bad_flag = 0; + unsigned int meta_data[7]; + __int64 file_size; + HANDLE hFile; + + wcscpy(file_path, base_dir); + wcscpy(file_path + base_len, list_buf + files[num].name); + i = (int)wcslen(file_path); + if ((i > 1) && (file_path[i - 1] == '\\')){ // フォルダなら + bad_flag = 64; + i = GetFileAttributes(file_path); + if ((i == INVALID_FILE_ATTRIBUTES) || ((i & FILE_ATTRIBUTE_DIRECTORY) == 0)){ + bad_flag = 65; // フォルダが存在しない + printf(" - Missing : \"%s\"\n", ascii_buf); + } else { // フォルダが存在する + printf(" 0 Complete : \"%s\"\n", ascii_buf); + } + files[num].state = bad_flag; + return 0; + } + + // ファイルが存在するか + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL); + if (hFile == INVALID_HANDLE_VALUE){ // ファイルが存在しない + bad_flag = 1; + } else { + // ファイルのサイズを調べる + i = check_ini_state(num, meta_data, hFile); + if (i == -1){ // エラー + CloseHandle(hFile); + bad_flag = 1; // 属性を取得できないファイルは消失と判定する + } + } + if (bad_flag == 0){ // ファイルが存在するなら状態を確認する + memcpy(&file_size, meta_data, 8); + if (files[num].size == 0){ // サイズが 0 だと破損しようがない + if (file_size > 0) // 末尾にゴミが付いてる + bad_flag = 16; + } else if (file_size >= files[num].size){ // 本来のサイズまでのハッシュ値が一致するか確かめる + if (i == -2){ // 検査結果の記録が無ければ + i = file_hash_check(num, list_buf + files[num].name, hFile, INT_MAX, files, NULL); + if (i == -2) + return 2; // 検査中にキャンセルされた + // MD5-16k が一致した時だけ検査結果を記録する + if (i != -1) + write_ini_state(num, meta_data, i); + } + if (i != -3){ // ファイルのハッシュ値が異なる + bad_flag = 2; // IO エラーでも破損扱いにして処理を続行する + } else if (file_size > files[num].size){ // 本来のサイズまでのハッシュ値は同じ + bad_flag = 16; // 末尾にゴミが付いてないか調べる + } + } else { // file_size < files[num].size + bad_flag = 2; + } + CloseHandle(hFile); + } + + files[num].state = bad_flag; + switch (bad_flag){ + case 0: // 完全なファイル + printf(" = Complete : \"%s\"\n", ascii_buf); + break; + case 1: // 消失したファイル + printf(" - Missing : \"%s\"\n", ascii_buf); + break; + case 2: // 破損したファイル + printf("%13I64d Damaged : \"%s\"\n", file_size, ascii_buf); + break; + case 16: // 追加されたファイル + printf("%13I64d Appended : \"%s\"\n", file_size, ascii_buf); + break; + } + + return 0; +} + +// ソース・ファイルが完全かどうかを調べる +// 0=完了, 1=エラー, 2=キャンセル +int check_file_complete( + char *ascii_buf, + wchar_t *file_path, // 作業用 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + int i, rv; +#ifdef TIMER +unsigned int time_start = GetTickCount(); +#endif + + printf("\nVerifying Input File :\n"); + printf(" Size Status : Filename\n"); + fflush(stdout); + count_last = 0; + first_num = 0; // 初めて見つけたソース・ブロックの数 + for (i = 0; i < entity_num; i++){ + if (cancel_progress() != 0) // キャンセル処理 + return 2; + utf16_to_cp(list_buf + files[i].name, ascii_buf, cp_output); + + if (files[i].size == 0){ // フォルダまたは空ファイルが存在するか調べる + rv = check_file_exist(ascii_buf, file_path, i, files); + } else { + if (files[i].state & 0x80){ // チェックサムが欠落してても完全かどうかは判る + rv = check_file_hash_incomplete(ascii_buf, file_path, i, files, s_blk); + } else { // ソース・ファイルの状態 (完全、消失、破損) を判定する + rv = check_file_hash(ascii_buf, file_path, i, files, s_blk); + } + } + if (rv != 0) + return rv; + fflush(stdout); + } + for (i = entity_num; i < file_num; i++){ + if (cancel_progress() != 0) // キャンセル処理 + return 2; + utf16_to_cp(list_buf + files[i].name, ascii_buf, cp_output); + if (rv = check_file_exist(ascii_buf, file_path, i, files)) + return rv; + fflush(stdout); + } + +#ifdef TIMER +time_start = GetTickCount() - time_start; +printf("\n hash %d.%03d sec", time_start / 1000, time_start % 1000); +if (time_start > 0){ + time_start = (int)((total_file_size * 125) / ((__int64)time_start * 131072)); + printf(", %d MB/s\n", time_start); +} else { + printf("\n"); +} +#endif + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// SSD 上で複数ファイルを同時に検査する + +// MAX_MULTI_READ の2倍ぐらいにする? +#define MAX_READ_NUM 10 + +int check_file_complete_multi( + char *ascii_buf, + wchar_t *file_path, // 作業用 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + int err = 0, i, rv; + int num, bad_flag, multi_read, multi_num, id, id_prog; + unsigned int time_last; + __int64 file_size; // 存在するファイルのサイズは本来のサイズとは異なることもある + HANDLE hFile; + HANDLE hSub[MAX_READ_NUM]; + FILE_CHECK_TH th[MAX_READ_NUM]; +#ifdef TIMER +unsigned int time_start = GetTickCount(); +#endif + + memset(hSub, 0, sizeof(HANDLE) * MAX_READ_NUM); + // Core数に応じてスレッド数を増やす + if ((memory_use & 32) != 0){ // NVMe SSD + if (cpu_num >= 8){ // 8 ~ 16 Cores + multi_read = 4; + } else { // 3 Cores + Hyper-threading, or 4 ~ 7 Cores + multi_read = 3; + } + } else { // SATA SSD + multi_read = 2; + } + if (multi_read > entity_num) + multi_read = entity_num; + multi_num = 0; +#ifdef TIMER + printf("\n cpu_num = %d, entity_num = %d, multi_read = %d\n", cpu_num, entity_num, multi_read); +#endif + id_prog = -1; + for (i = 0; i < MAX_READ_NUM; i++) + th[i].num = -1; // データ未使用の印 + wcscpy(file_path, base_dir); + + printf("\nVerifying Input File :\n"); + printf(" Size Status : Filename\n"); + fflush(stdout); + time_last = GetTickCount(); + count_last = 0; + first_num = 0; // 初めて見つけたソース・ブロックの数 + for (num = 0; num < entity_num; num++){ + if (cancel_progress() != 0){ // キャンセル処理 + err = 2; + goto error_end; + } + + // ソース・ファイルの状態 (完全、消失、破損) を判定する + // チェックサムが欠落してても完全かどうかは判る + if (files[num].size > 0){ + for (id = 0; id < MAX_READ_NUM; id++){ // 未使用のスレッドを探す + if (th[id].num < 0) + break; + } + if (id_prog < 0){ + id_prog = id; + prog_last = -1; + } + th[id].num = num; + hSub[id] = NULL; + + // 検査以外はシングル・スレッドで行う + th[id].flag = 0; + wcscpy(file_path + base_len, list_buf + files[num].name); + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL); + if (hFile == INVALID_HANDLE_VALUE){ // ファイルが存在しない + th[id].flag = 1; + } else { // ファイルの状態が記録されてるか調べる + th[id].rv = check_ini_state(num, th[id].meta, hFile); + if (th[id].rv == -1){ // エラー + CloseHandle(hFile); + th[id].flag = 1; // 属性を取得できないファイルは消失と判定する + } + } + if (th[id].flag == 0){ // ファイルが存在するなら状態を確認する + memcpy(&file_size, th[id].meta, 8); + if (file_size >= files[num].size){ // 本来のサイズまでのハッシュ値が一致するか確かめる + if (th[id].rv == -2){ // 検査結果の記録が無ければ + //rv = file_hash_check(num, list_buf + files[num].name, hFile, 0, files, s_blk); + th[id].hFile = hFile; + th[id].files = files; + if (files[num].state & 0x80){ + th[id].flag |= 0x80; + th[id].s_blk = NULL; + } else { + th[id].s_blk = s_blk; + } + th[id].rv = 0; + //_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する + hSub[id] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, file_hash_background, (LPVOID)&(th[id]), 0, NULL); + if (hSub[id] == NULL){ + print_win32_err(); + printf("error, sub-thread\n"); + CloseHandle(hFile); + err = 1; + goto error_end; + } + multi_num++; + } else { + CloseHandle(hFile); + } + } else { // file_size < files[num].size + CloseHandle(hFile); + if (files[num].state & 0x80){ + th[id].flag = 2 | 0x80; + } else { + th[id].flag = 2; + } + } + } + //printf("num = %d, multi_num = %d, id = %d, id_prog = %d\n", num, multi_num, id, id_prog); + } + + id = id_prog; + //printf("check: num = %d, multi_num = %d, id = %d, id_prog = %d\n", num, multi_num, id, id_prog); + while ((id >= 0) && (id < MAX_READ_NUM)){ + if (hSub[id]){ // 終了してるか調べる + rv = WaitForSingleObject(hSub[id], 0); + if (rv == WAIT_OBJECT_0){ // 終了してたら閉じる + CloseHandle(hSub[id]); + hSub[id] = NULL; + multi_num--; + } + } + if (hSub[id] == NULL){ + //printf("id = %d, th[id].rv = %d, th[id].flag = %d\n", id, th[id].rv, th[id].flag); + bad_flag = th[id].flag & ~0x80; + if (bad_flag != 1){ // 消失以外 + memcpy(&file_size, th[id].meta, 8); + if (bad_flag != 2){ // 破損以外 + rv = th[id].rv; + // MD5-16k が一致した時だけ検査結果を記録する + if (rv != -1) + write_ini_state(th[id].num, th[id].meta, rv); + if (rv != -3){ // ファイルのハッシュ値が異なる + bad_flag = 2; // IO エラーでも破損扱いにして処理を続行する + if (rv > 0) // 先頭に完全なブロックが見つかってるなら + bad_flag |= rv << 8; // 上位 24-bit に完全なブロックの個数を記録する + // MD5-16k だけ異なるのは検出ブロック 0個として扱う + } else if (file_size > files[th[id].num].size){ // 本来のサイズまでのハッシュ値は同じ + bad_flag = 16; // 末尾にゴミが付いてないか調べる + } + } + } + + utf16_to_cp(list_buf + files[th[id].num].name, ascii_buf, cp_output); + if (th[id].flag & 0x80){ + files[th[id].num].state = bad_flag | 0x80; // チェックサムが存在しない印 + } else { + files[th[id].num].state = bad_flag; + } + if (bad_flag == 1){ // 消失 + printf(" - Missing : \"%s\"\n", ascii_buf); + } else { // 完全、破損または追加 (追加は特殊な破損状態) + int b_last; + if (bad_flag & 2){ // 破損なら上位 24-bit に完全なブロックの個数が記録されてる + if (th[id].flag & 0x80){ // チェックサムが存在しない場合 + printf("%13I64d Damaged : \"%s\"\n", file_size, ascii_buf); + } else { + bad_flag >>= 8; + b_last = files[th[id].num].b_off + bad_flag; // 途中まで有効と見なす + for (i = files[th[id].num].b_off; i < b_last; i++) + s_blk[i].exist = 2; + first_num += bad_flag; // ブロック数を集計する + print_progress_file(-1, first_num, NULL); // 重複を除外した合計ブロック数を表示する + printf("%13I64d %8d : \"%s\"\n", file_size, bad_flag, ascii_buf); + } + } else { // 完全か追加 + b_last = files[th[id].num].b_off + files[th[id].num].b_num; + for (i = files[th[id].num].b_off; i < b_last; i++) + s_blk[i].exist = 1; // 全ブロックが有効と見なす + first_num += files[th[id].num].b_num; // ブロック数を集計する + print_progress_file(-1, first_num, NULL); // 重複を除外した合計ブロック数を表示する + if (bad_flag == 0){ // サイズが同じでハッシュ値が一致したら完全 + printf(" = Complete : \"%s\"\n", ascii_buf); + } else { // 本来のサイズまでハッシュ値が一致したら追加 + printf("%13I64d Appended : \"%s\"\n", file_size, ascii_buf); + } + } + } + fflush(stdout); + time_last = GetTickCount(); + + th[id].num = -1; // 未使用に戻す + if (id == id_prog) // 経過表示中のスレッドが結果を表示したら + id_prog = -1; + } + + // 動作中のスレッドが終了してるか調べる + for (id = 0; id < MAX_READ_NUM; id++){ + if (th[id].num >= 0){ + if (hSub[id]){ + rv = WaitForSingleObject(hSub[id], 0); + if (rv == WAIT_OBJECT_0){ // 終了してたら閉じる + CloseHandle(hSub[id]); + hSub[id] = NULL; + multi_num--; + } + } + // 経過表示してない状態なら、他の終了したスレッドの結果も表示する + if ((id_prog < 0) && (hSub[id] == NULL)){ + id_prog = id; + //printf("next: num = %d, multi_num = %d, id = %d, id_prog = %d\n", num, multi_num, id, id_prog); + break; + } + } + } + if (id == MAX_READ_NUM){ // 経過表示中、または、他のスレッドが動いて無いなら + if (id_prog < 0){ // 経過表示するスレッドを指定する + for (i = 0; i < MAX_READ_NUM; i++){ + if (th[i].num >= 0){ + id_prog = i; + prog_last = -1; + break; + } + } + if (id_prog < 0) // 動作中のスレッドがなければ待たない + break; + } + // 最後のファイル、または、同時検査数や最大スレッド数に達してるなら、終わるのを待つ + for (i = 0; i < MAX_READ_NUM; i++){ + if (th[i].num < 0) + break; + } + if ((num + 1 == entity_num) || (multi_num == multi_read) || (i == MAX_READ_NUM)){ + id = id_prog; + //printf("wait: num = %d, multi_num = %d, id = %d, id_prog = %d\n", num, multi_num, id, id_prog); + if (hSub[id]){ // 終了してるか調べる + rv = WaitForSingleObject(hSub[id], UPDATE_TIME / 2); + if (rv == WAIT_OBJECT_0){ // 終了してたら閉じる + CloseHandle(hSub[id]); + hSub[id] = NULL; + multi_num--; + } + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + // 上位 12-bit にパーセント、下位 20-bit に検出ブロック数が記録されてる + i = th[id].rv; + rv = i >> 20; + i = first_num + (i & 0x000FFFFF); + if (print_progress_file(rv, i, list_buf + files[th[id].num].name)){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + } + } + } + } + } + for (num = 0; num < entity_num; num++){ + if (files[num].size > 0) + continue; + if (cancel_progress() != 0){ // キャンセル処理 + err = 2; + goto error_end; + } + // フォルダまたは空ファイルが存在するか調べる + utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); + err = check_file_exist(ascii_buf, file_path, num, files); + if (err) + goto error_end; + fflush(stdout); + } + for (num = entity_num; num < file_num; num++){ + if (cancel_progress() != 0){ // キャンセル処理 + err = 2; + goto error_end; + } + utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); + err = check_file_exist(ascii_buf, file_path, num, files); + if (err) + goto error_end; + fflush(stdout); + } + +#ifdef TIMER +time_start = GetTickCount() - time_start; +printf("\n hash %d.%03d sec", time_start / 1000, time_start % 1000); +if (time_start > 0){ + time_start = (int)((total_file_size * 125) / ((__int64)time_start * 131072)); + printf(", %d MB/s\n", time_start); +} else { + printf("\n"); +} +#endif + +error_end: + if (err != 0){ // エラーが発生した場合 + for (i = 0; i < MAX_READ_NUM; i++) // 終了指示をだす + InterlockedExchange(&(th[i].flag), -1); + } + for (i = 0; i < MAX_READ_NUM; i++){ + if (hSub[i]){ + WaitForSingleObject(hSub[i], INFINITE); + CloseHandle(hSub[i]); + } + } + + return err; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 指定されたフォルダが空かどうかを調べる +static int is_folder_empty(wchar_t *folder_path) // フォルダのパス +{ + int len; + HANDLE hFind; + WIN32_FIND_DATA FindData; + + len = (int)wcslen(folder_path); + folder_path[len ] = '*'; + folder_path[len + 1] = 0; + hFind = FindFirstFile(folder_path, &FindData); + folder_path[len ] = 0; // 元に戻す + if (hFind != INVALID_HANDLE_VALUE){ + do { + if ((wcscmp(FindData.cFileName, L".") != 0) && (wcscmp(FindData.cFileName, L"..") != 0)){ + len = 0; // 空ではない + break; + } + } while (FindNextFile(hFind, &FindData)); + } + FindClose(hFind); + + return len; +} + +// 指定フォルダ内の別名ファイルを探す +// 0=正常終了, 1=エラー, 2=キャンセル +static int recursive_search( + char *ascii_buf, // 作業用 + wchar_t *search_path, // 検索するファイルの親ディレクトリ、見つけた別名ファイルの相対パスが戻る + int dir_len, // ディレクトリ部分の長さ + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + __int64 file_size; // 存在するファイルのサイズは本来のサイズとは異なることもある + int rv, i, num, b_last, source_len; + unsigned int meta_data[7]; + HANDLE hFile, hFind; + WIN32_FIND_DATA FindData; + + // ソース・ファイル名の領域までしか比較しない (別名ファイルが検出されるのは一回だけ) + source_len = list_len - 1; + + // 指定されたディレクトリ直下のファイルと比較する + wcscpy(search_path + dir_len, L"*"); + hFind = FindFirstFile(search_path, &FindData); + if (hFind != INVALID_HANDLE_VALUE){ + do { + if (cancel_progress() != 0){ // キャンセル処理 + FindClose(hFind); + return 2; + } + if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0){ // フォルダなら + // 親ディレクトリは無視する + if ((wcscmp(FindData.cFileName, L".") == 0) || (wcscmp(FindData.cFileName, L"..") == 0)) + continue; + // 発見したフォルダ名が長すぎる場合は無視する + if (dir_len + wcslen(FindData.cFileName) + 2 >= MAX_LEN) // 末尾に「\*」が追加される + continue; + // 現在のディレクトリ部分に見つかったフォルダ名を連結する + wcscpy(search_path + dir_len, FindData.cFileName); + wcscat(search_path + dir_len, L"\\"); + // 他のソース・ファイルは無視する + if (search_file_path(list_buf + 1, source_len, search_path + base_len)) // 基準ディレクトリからの相対パスで比較する + continue; + + // フォルダ名が検出対象と一致するかどうか確かめる + for (num = entity_num; num < file_num; num++){ // 空フォルダは non-recovery set にしか存在しない + if (files[num].state == 65){ // 消失したフォルダなら + if (_wcsicmp(search_path + dir_len, offset_file_name(list_buf + files[num].name)) == 0){ + // 他のファイルのサブ・ディレクトリは動かせないので、中身のあるフォルダは無視する + if (is_folder_empty(search_path) == 0) + continue; + // 移動されたフォルダが見つかった + files[num].name2 = list_len; // 移動されたフォルダ名を記録する + if (add_file_path(search_path + base_len)){ + printf("add_file_path\n"); + FindClose(hFind); + return 1; + } + utf16_to_cp(search_path + base_len, ascii_buf, cp_output); // 移動されたフォルダ名を表示する + printf(" 0 Found : \"%s\"\n", ascii_buf); + files[num].state = 64 | 32; // 移動されたフォルダ + utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); // 本来のフォルダ名を表示する + printf(" = Moved : \"%s\"\n", ascii_buf); + break; // 見つかった場合は、それ以降のソース・ファイルと比較しない + } + } + } + continue; + } + + // 発見したファイル名が長すぎる場合は無視する + if (dir_len + wcslen(FindData.cFileName) >= MAX_LEN) + continue; + // 現在のディレクトリ部分に見つかったファイル名を連結する + wcscpy(search_path + dir_len, FindData.cFileName); + // 破損してないリカバリ・ファイルは無視する + if (search_file_path(recv_buf, recv_len, search_path)) // フル・パスで比較する + continue; + // 他のソース・ファイルは無視する + if (search_file_path(list_buf + 1, source_len, search_path + base_len)) // 基準ディレクトリからの相対パスで比較する + continue; + + // ファイル・サイズが検出対象のサイズと一致するかどうか + file_size = ((__int64)(FindData.nFileSizeHigh) << 32) | (__int64)(FindData.nFileSizeLow); + for (num = 0; num < file_num; num++){ + if ((files[num].state & 0x03) && (files[num].size == file_size)){ + if (file_size > 0){ + //printf("find size %I64d, %S\n", file_size, search_path + base_len); + // 別名ファイルのハッシュ値を調べる + hFile = CreateFile(search_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL); + if (hFile != INVALID_HANDLE_VALUE){ // ファイルを開けたなら内容を確認する + prog_last = -1; // 経過表示がまだの印 + rv = check_ini_state(num, meta_data, hFile); + if (rv == -2){ // 検査結果の記録が無ければ + if ((num >= entity_num) || ((files[num].state & 0x80) != 0)){ + // non-recovery set のファイルまたはチェックサムが欠落したソース・ファイル + rv = file_hash_check(num, FindData.cFileName, hFile, INT_MAX, files, NULL); + } else { // 普通のソース・ファイル + if (files[num].state & 0x02){ // 本来のファイルが破損ならスライスの重複カウントを避ける + rv = files[num].state >> 8; + } else { + rv = 0; + } + rv = file_hash_check(num, FindData.cFileName, hFile, rv, files, s_blk); + } + if (rv == -2){ + CloseHandle(hFile); + FindClose(hFind); + return 2; // エラーは無視して続行するが、キャンセルなら中断する + } + // MD5-16k が一致した時だけ検査結果を記録する + if (rv != -1) + write_ini_state(num, meta_data, rv); + } + CloseHandle(hFile); + if (rv == -3){ // ファイルのハッシュ値が一致した + b_last = files[num].b_off + files[num].b_num; + for (i = files[num].b_off; i < b_last; i++) + s_blk[i].exist = 1; // 全ブロックが有効と見なす + files[num].name2 = list_len; // 別名・移動のファイル名を記録する + if (add_file_path(search_path + base_len)){ + printf("add_file_path\n"); + FindClose(hFind); + return 1; + } + if (files[num].state & 0x01){ // 本来のファイルが消失してるなら + files[num].state ^= 0x21; // 消失して別名・移動 0x01 -> 0x20 + first_num += files[num].b_num; // ブロック数を集計する + } else { + files[num].state ^= 0x2A; // 破損して別名・移動 0x02 -> 0x28 (スライス個数は維持する) + first_num += files[num].b_num - (files[num].state >> 8); // 新たなブロック数だけ追加する + } + print_progress_file(-1, first_num, NULL); // 重複を除外した利用可能なソース・ブロック数を表示する + utf16_to_cp(search_path + base_len, ascii_buf, cp_output); // 別名・移動のファイル名を表示する + printf("%13I64d Found : \"%s\"\n", file_size, ascii_buf); + utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); // 本来のファイル名を表示する + if (compare_directory(list_buf + files[num].name, search_path + base_len) == 0){ // 同じ場所なら別名 + printf(" = Misnamed : \"%s\"\n", ascii_buf); + } else { // 別の場所なら移動 + printf(" = Moved : \"%s\"\n", ascii_buf); + } + break; // 見つかった場合は、それ以降のソース・ファイルと比較しない + } else { + if (rv > 0){ // 別名の破損ファイル内にスライスを見つけたら、自動的に検査対象に追加する + //printf("rv = %d, %S\n", rv, search_path + base_len); + if (list2_buf == NULL){ + list2_len = 0; + list2_max = ALLOC_LEN; + list2_buf = (wchar_t *)malloc(list2_max * 2); + } + if (list2_buf){ + wchar_t *tmp_p; + int len = (int)wcslen(search_path); + 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); + FindClose(hFind); + 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; + } + } + if (prog_last >= 0) // 途中までのスライス数を表示してた場合 + print_progress_file(-1, first_num, NULL); // 元の数に戻しておく + } + print_progress_done(); // 経過表示があれば 100% にしておく + } + } + } else { // サイズが 0 (空ファイル) の場合 + if (files[num].state & 64) + continue; // 比較対象がフォルダなら無視する (ファイル名の比較ではねられるから不要?) + // ファイル名が同じかどうか確かめる + if (_wcsicmp(FindData.cFileName, offset_file_name(list_buf + files[num].name)) == 0){ + // 移動されたファイルが見つかった + files[num].name2 = list_len; // 移動されたファイル名を記録する + if (add_file_path(search_path + base_len)){ + printf("add_file_path\n"); + FindClose(hFind); + return 1; + } + utf16_to_cp(search_path + base_len, ascii_buf, cp_output); // 移動されたファイル名を表示する + printf(" 0 Found : \"%s\"\n", ascii_buf); + files[num].state = 0x20; // 空ファイルは破損しないので、消失して移動のみ + utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); // 本来のファイル名を表示する + printf(" = Moved : \"%s\"\n", ascii_buf); + break; // 見つかった場合は、それ以降のソース・ファイルと比較しない + } + } + } + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + } + FindClose(hFind); + + // 指定されたディレクトリ以下のフォルダ内も再帰的に調べる + wcscpy(search_path + dir_len, L"*"); + hFind = FindFirstFile(search_path, &FindData); + if (hFind != INVALID_HANDLE_VALUE){ + do { + // フォルダ以外は無視する + if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) + continue; + // 親ディレクトリは無視する + if ((wcscmp(FindData.cFileName, L".") == 0) || (wcscmp(FindData.cFileName, L"..") == 0)) + continue; + // 発見したフォルダ名が長すぎる場合は無視する + if (dir_len + wcslen(FindData.cFileName) + 2 >= MAX_LEN) // 末尾に「\*」が追加される + continue; + // そのフォルダの内部を新たな検索対象にする + wcscpy(search_path + dir_len, FindData.cFileName); + wcscat(search_path + dir_len, L"\\"); // ディレクトリ記号は「\」に統一してある + //printf_cp("inner search... %s\n", search_path + base_len); + rv = recursive_search(ascii_buf, search_path, (int)wcslen(search_path), files, s_blk); + if (rv != 0){ + // エラー発生、またはキャンセルされた + FindClose(hFind); + return rv; + } + } while (FindNextFile(hFind, &FindData)); // 次のフォルダを検索する + } + FindClose(hFind); + + return 0; +} + +// フォルダを無視して、指定フォルダ内の別名ファイルを探す(サブ・フォルダに移動したファイルは検出不能) +// 0=正常終了, 1=エラー, 2=キャンセル +static int direct_search( + char *ascii_buf, // 作業用 + wchar_t *search_path, // 検索するファイルの親ディレクトリ、見つけた別名ファイルの相対パスが戻る + int dir_len, // ディレクトリ部分の長さ + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + __int64 file_size; // 存在するファイルのサイズは本来のサイズとは異なることもある + int rv, i, num, b_last, source_len; + unsigned int meta_data[7]; + HANDLE hFile, hFind; + WIN32_FIND_DATA FindData; + + // ソース・ファイル名の領域までしか比較しない (別名ファイルが検出されるのは一回だけ) + source_len = list_len - 1; + + // 指定されたディレクトリ直下のファイルとだけ比較する + wcscpy(search_path + dir_len, L"*"); + hFind = FindFirstFile(search_path, &FindData); + if (hFind != INVALID_HANDLE_VALUE){ + do { + if (cancel_progress() != 0){ // キャンセル処理 + FindClose(hFind); + return 2; + } + if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) // フォルダなら無視する + continue; + + // 発見したファイル名が長すぎる場合は無視する + if (dir_len + wcslen(FindData.cFileName) >= MAX_LEN) + continue; + // 現在のディレクトリ部分に見つかったファイル名を連結する + wcscpy(search_path + dir_len, FindData.cFileName); + // 破損してないリカバリ・ファイルは無視する + if (search_file_path(recv_buf, recv_len, search_path)) // フル・パスで比較する + continue; + // 他のソース・ファイルは無視する + if (search_file_path(list_buf + 1, source_len, search_path + base_len)) // 基準ディレクトリからの相対パスで比較する + continue; + + // ファイル・サイズが検出対象のサイズと一致するかどうか + file_size = ((__int64)(FindData.nFileSizeHigh) << 32) | (__int64)(FindData.nFileSizeLow); + for (num = 0; num < file_num; num++){ + if ((files[num].state & 0x03) && (files[num].size == file_size)){ + if (file_size > 0){ // サイズが 0 (空ファイル) でなければ + //printf("find size %I64d, %S\n", file_size, search_path + base_len); + // 別名ファイルのハッシュ値を調べる + hFile = CreateFile(search_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL); + if (hFile != INVALID_HANDLE_VALUE){ // ファイルを開けたなら内容を確認する + prog_last = -1; // 経過表示がまだの印 + rv = check_ini_state(num, meta_data, hFile); + if (rv == -2){ // 検査結果の記録が無ければ + if ((num >= entity_num) || ((files[num].state & 0x80) != 0)){ + // non-recovery set のファイルまたはチェックサムが欠落したソース・ファイル + rv = file_hash_check(num, FindData.cFileName, hFile, INT_MAX, files, NULL); + } else { // 普通のソース・ファイル + if (files[num].state & 0x02){ // 本来のファイルが破損ならスライスの重複カウントを避ける + rv = files[num].state >> 8; + } else { + rv = 0; + } + rv = file_hash_check(num, FindData.cFileName, hFile, rv, files, s_blk); + } + if (rv == -2){ + CloseHandle(hFile); + FindClose(hFind); + return 2; // エラーは無視して続行するが、キャンセルなら中断する + } + // MD5-16k が一致した時だけ検査結果を記録する + if (rv != -1) + write_ini_state(num, meta_data, rv); + } + CloseHandle(hFile); + if (rv == -3){ // ファイルのハッシュ値が一致した + b_last = files[num].b_off + files[num].b_num; + for (i = files[num].b_off; i < b_last; i++) + s_blk[i].exist = 1; // 全ブロックが有効と見なす + files[num].name2 = list_len; // 別名・移動のファイル名を記録する + if (add_file_path(search_path + base_len)){ + printf("add_file_path\n"); + FindClose(hFind); + return 1; + } + if (files[num].state & 0x01){ // 本来のファイルが消失してるなら + files[num].state ^= 0x21; // 消失して別名・移動 0x01 -> 0x20 + first_num += files[num].b_num; // ブロック数を集計する + } else { + files[num].state ^= 0x2A; // 破損して別名・移動 0x02 -> 0x28 (スライス個数は維持する) + first_num += files[num].b_num - (files[num].state >> 8); // 新たなブロック数だけ追加する + } + print_progress_file(-1, first_num, NULL); // 重複を除外した利用可能なソース・ブロック数を表示する + utf16_to_cp(search_path + base_len, ascii_buf, cp_output); // 別名・移動のファイル名を表示する + printf("%13I64d Found : \"%s\"\n", file_size, ascii_buf); + utf16_to_cp(list_buf + files[num].name, ascii_buf, cp_output); // 本来のファイル名を表示する + if (compare_directory(list_buf + files[num].name, search_path + base_len) == 0){ // 同じ場所なら別名 + printf(" = Misnamed : \"%s\"\n", ascii_buf); + } else { // 別の場所なら移動 + printf(" = Moved : \"%s\"\n", ascii_buf); + } + break; // 見つかった場合は、それ以降のソース・ファイルと比較しない + } else { + if (rv > 0){ // 別名の破損ファイル内にスライスを見つけたら、自動的に検査対象に追加する + //printf("rv = %d, %S\n", rv, search_path + base_len); + if (list2_buf == NULL){ + list2_len = 0; + list2_max = ALLOC_LEN; + list2_buf = (wchar_t *)malloc(list2_max * 2); + } + if (list2_buf){ + wchar_t *tmp_p; + int len = (int)wcslen(search_path); + 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); + FindClose(hFind); + 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; + } + } + if (prog_last >= 0) // 途中までのスライス数を表示してた場合 + print_progress_file(-1, first_num, NULL); // 元の数に戻しておく + } + print_progress_done(); // 経過表示があれば 100% にしておく + } + } + } + } + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + } + FindClose(hFind); + + return 0; +} + +// ソース・ファイルが不完全なら別名・移動ファイルを探す +// 0=完了, 1=エラー, 2=キャンセル +int search_misnamed_file( + char *ascii_buf, // 作業用 + wchar_t *file_path, // 作業用 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + wchar_t find_path[MAX_LEN]; + int i, rv, num, b_last; + + // 順列検査でファイルが破損してれば上書修復する(作業ファイルを作らない) + if (switch_v & 4){ + for (num = 0; num < entity_num; num++){ + if (files[num].state & 2) + files[num].state |= 4; + } + } + + // 不完全なソース・ファイルが存在するか調べる + i = 0; // 消失 0x01, 0x81, フォルダの消失 0x41 も + rv = 0; // 破損 0x02, 0x82, 追加 0x10 も + b_last = 0; // 消失や破損なら別名ファイルを探す + for (num = 0; num < file_num; num++){ + if (files[num].state & 0x01) + i++; // 消失したファイルの数 + if (files[num].state & 0x12) + rv++; // 破損・追加のファイルの数 + if (files[num].state & 0x03) // 消失 0x01、破損 0x02 なら別名ファイルを探す + b_last++; // 探す必要があるファイルの数 + } + printf("\nComplete file count\t: %d\n", file_num - i - rv); + if (b_last == 0){ // 探さない場合はファイル数の集計だけ + printf("Misnamed file count\t: %d\n", 0); + printf("Damaged file count\t: %d\n", rv); + printf("Missing file count\t: %d\n", i); + return 0; // 探さないで終わる + } + + // 検査したファイルごとに結果を表示する + printf("\nSearching misnamed file: %d\n", b_last); // 探す対象のファイル数 + printf(" Size Status : Filename\n"); + fflush(stdout); + wcscpy(find_path, base_dir); + if (switch_v & 8){ // 基準ディレクトリ直下のファイルだけを検査する + if (rv = direct_search(ascii_buf, find_path, base_len, files, s_blk)) + return rv; + } else { // 基準ディレクトリ以下を再帰的に検索する + if (rv = recursive_search(ascii_buf, find_path, base_len, files, s_blk)) + return rv; + } + + // 状態ごとにファイル数を集計する + i = 0; // 消失 0x01, 0x81, フォルダの消失 0x41 も + rv = 0; // 破損 0x02, 0x82, 追加 0x10 も + b_last = 0; // 消失して別名・移動 0x20, 0xA0, 破損して別名・移動 0x28, 0xA8, フォルダの移動 0x60 + for (num = 0; num < file_num; num++){ + if (files[num].state & 0x01) + i++; // 消失したファイルの数 + if (files[num].state & 0x12) + rv++; // 破損・追加のファイルの数 + if (files[num].state & 0x20) + b_last++; // 別名・移動の数 + } + printf("\n"); + printf("Misnamed file count\t: %d\n", b_last); + printf("Damaged file count\t: %d\n", rv); + printf("Missing file count\t: %d\n", i); + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 破損・分割・類似名のファイルから使えるスライスを探す +// 0=完了, 1=エラー, 2=キャンセル +int search_file_slice( + char *ascii_buf, // 作業用 + wchar_t *file_path, // 作業用 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 +{ + int rv, num; + slice_ctx sc[1]; + + printf("\nInput File Slice found\t: %d\n", first_num); + fflush(stdout); + if (first_num > source_num) + return 1; // 検出数が多すぎるのはどこか間違ってる + if ((first_num == source_num) || (block_size <= 4)) + return 0; // 探さないで終わる + + // スライス検査の準備をする + memset(sc, 0, sizeof(slice_ctx)); + rv = init_verification(files, s_blk, sc); + if (rv < 0) // 致命的なエラーまたはスライス検査不要 + return 0; + if (rv == 1) // スライド検査不可あるいは不要 + switch_v |= 1; // スライド検査しない場合は簡易検査にする + //printf("\n init_verification = %d, switch_v = %d\n", rv, switch_v); + + // 検査したファイルごとに結果を表示する + printf("\nFinding available slice:\n"); + printf(" Size Status : Filename\n"); + fflush(stdout); + + // 破損してるファイルと分割・類似名ファイルを検査する + for (num = 0; num < entity_num; num++){ + // 破損 0x02、追加 0x10、破損して別名 0x28 ならソース・ファイルを検査する + // 消失 0x01、破損 0x02 で、そのファイルのスライスが未検出なら、分割ファイルを探す + // 消失で分割ファイルが見つからない場合だけ、文字化けファイルも探す + if ((files[num].state & 0x1B) == 0) + continue; + + if (rv = cancel_progress()) // キャンセル処理 + goto error_end; + + if (rv = check_file_slice(ascii_buf, file_path, num, files, s_blk, sc)) + goto error_end; + } + + // 指定された外部ファイルを検査する + if ((first_num < source_num) && (list2_buf != NULL) && ((switch_v & 4) == 0)){ + if (rv = check_external_file(ascii_buf, file_path, files, s_blk, sc)) + goto error_end; + } + + // 簡易検査と順列検査以外なら、そのファイルのスライスが全て検出されていても、分割ファイルを探す + num = 0; + if (switch_v & (1 | 4)) + num = entity_num; + for (; num < entity_num; num++){ + if (first_num >= source_num) + break; + + if (rv = cancel_progress()) // キャンセル処理 + goto error_end; + + if (rv = search_file_split(ascii_buf, file_path, num, files, s_blk, sc)) + goto error_end; + } + + // 基準ディレクトリ内のその他のファイルを検査する + if ((first_num < source_num) && ((switch_v & 4) == 0)){ + if (rv = search_additional_file(ascii_buf, file_path, files, s_blk, sc)) + goto error_end; + } + + // 見つかったソース・ブロックの数 + printf("\nInput File Slice found\t: %d\n", first_num); + fflush(stdout); + if (first_num > source_num){ + rv = 1; // 検出数が多すぎるのはどこか間違ってる + } else { + rv = 0; + } + json_save_found(); + +error_end: + if (sc->buf){ + if (sc->h){ // サブ・スレッドを終了させる + sc->size = 0; // 終了指示 + SetEvent(sc->run); + WaitForSingleObject(sc->h, INFINITE); + CloseHandle(sc->h); + } + free(sc->buf); + } + if (sc->order) + free(sc->order); + if (sc->hFile_tmp) + CloseHandle(sc->hFile_tmp); + if (sc->flk_buf) + free(sc->flk_buf); + + return rv; +} + diff --git a/source/par2j/list.h b/source/par2j/list.h new file mode 100644 index 0000000..71c2984 --- /dev/null +++ b/source/par2j/list.h @@ -0,0 +1,48 @@ +#ifndef _LIST_H_ +#define _LIST_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +// ソース・ファイルを検査せずに一覧を表示する +int list_file_data( + char *ascii_buf, // 作業用 + file_ctx_r *files); // 各ソース・ファイルの情報 + +// ソース・ファイルが完全かどうかを一覧表示する +int check_file_complete( + char *ascii_buf, + wchar_t *file_path, // 作業用 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 + +int check_file_complete_multi( + char *ascii_buf, + wchar_t *file_path, // 作業用 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 + +// ソース・ファイルが不完全なら別名・移動ファイルを探す +int search_misnamed_file( + char *ascii_buf, + wchar_t *file_path, // 作業用 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 破損・分割・類似名のファイルから使えるスライスを探す +int search_file_slice( + char *ascii_buf, // 作業用 + wchar_t *file_path, // 作業用 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par2j/md5_crc.c b/source/par2j/md5_crc.c new file mode 100644 index 0000000..78d3f8e --- /dev/null +++ b/source/par2j/md5_crc.c @@ -0,0 +1,1979 @@ +// md5_crc.c +// Copyright : 2022-10-01 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 "phmd5.h" +#include "md5_crc.h" + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// バイト配列の MD5 ハッシュ値を求める +void data_md5( + unsigned char *data_in, // ハッシュ値を求めるバイト配列 + unsigned int data_len, // 入力バイト数 + unsigned char *hash) // ハッシュ値 16バイト +{ + PHMD5 ctx; + + Phmd5Begin(&ctx); + Phmd5Process(&ctx, data_in, data_len); + Phmd5End(&ctx); + memcpy(hash, ctx.hash, 16); +} + +// ブロックの MD5 ハッシュ値を求める +void data_md5_block( + unsigned char *data_in, // ハッシュ値を求めるバイト配列 + unsigned int data_len, // 入力バイト数 + unsigned char *hash) // ハッシュ値 16バイト +{ + PHMD5 ctx; + + Phmd5Begin(&ctx); + Phmd5Process(&ctx, data_in, data_len); + if (data_len < block_size) // ブロック・サイズまで 0で埋めて計算する + Phmd5ProcessZero(&ctx, block_size - data_len); + Phmd5End(&ctx); + memcpy(hash, ctx.hash, 16); +} + +// ファイルの MD5-16k ハッシュ値を求める +int file_md5_16( + wchar_t *file_path, // ハッシュ値を求めるファイル + unsigned char *hash) // ファイルの先頭 16KB 分のハッシュ値 (16バイト) +{ + unsigned char buf[16384]; + unsigned int len; + PHMD5 hash_ctx; + HANDLE hFile; + + // ソース・ファイルを開く + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return 1; + + // 先頭 16KB のハッシュ値を計算する + Phmd5Begin(&hash_ctx); // 初期化 + if (!ReadFile(hFile, buf, 16384, &len, NULL)){ + CloseHandle(hFile); + return 2; + } + Phmd5Process(&hash_ctx, buf, len); // MD5 計算 + Phmd5End(&hash_ctx); // 最終処理 + memcpy(hash, hash_ctx.hash, 16); + + CloseHandle(hFile); + return 0; +} + +// ファイルの指定部分の MD5 ハッシュ値を求める +int file_md5( + HANDLE hFileRead, // ハッシュ値を求めるファイルのハンドル + __int64 offset, + unsigned int data_len, // 入力バイト数 + unsigned char *hash) // ハッシュ値 (16バイト) +{ + unsigned char buf[IO_SIZE]; + unsigned int rv, len; + PHMD5 hash_ctx; + + // ファイルの開始位置を offsetバイト目にする + if (!SetFilePointerEx(hFileRead, *((PLARGE_INTEGER)&offset), NULL, FILE_BEGIN)) + return 1; + + Phmd5Begin(&hash_ctx); // 初期化 + + while (data_len > 0){ + len = IO_SIZE; + if (data_len < IO_SIZE) + len = data_len; + if (!ReadFile(hFileRead, buf, len, &rv, NULL) || (len != rv)) + return 2; + data_len -= len; + + Phmd5Process(&hash_ctx, buf, len); // MD5 計算 + } + Phmd5End(&hash_ctx); // 最終処理 + memcpy(hash, hash_ctx.hash, 16); + + return 0; +} + +/*------------------ 以下は MD5 と CRC-32 の組合せ関数 ------------------*/ + +// 内容が全て 0 のデータの MD5 ハッシュ値と CRC-32 を求める +void data_md5_crc32_zero( + unsigned char *hash) // ハッシュ値 (16 + 4バイト, MD5 + CRC-32) +{ + PHMD5 ctx; + + Phmd5Begin(&ctx); + Phmd5ProcessZero(&ctx, block_size); + Phmd5End(&ctx); + memcpy(hash, ctx.hash, 16); + memcpy(hash + 16, &window_mask, 4); // CRC-32 は window_mask と同じ +} + +// 2GB 未満のファイルの開始位置以降のハッシュ値を計算する +unsigned int file_crc_part(HANDLE hFile) +{ + unsigned char buf[4096]; + unsigned int len, crc = 0xFFFFFFFF; + + // 末尾まで読み込む + do { + if (!ReadFile(hFile, buf, 4096, &len, NULL) || (len == 0)) + break; + crc = crc_update(crc, buf, len); + } while (len > 0); + + return crc ^ 0xFFFFFFFF; +} + +// ファイルの offset バイト目からブロック・サイズ分の MD5 と CRC-32 を求める +int file_md5_crc32_block( + HANDLE hFileRead, // MD5 と CRC を求めるファイルのハンドル + __int64 offset, + unsigned int avail_size, // 入力バイト数 + unsigned char *hash) // ハッシュ値 (16 + 4バイト, MD5 + CRC-32) +{ + unsigned char buf[IO_SIZE]; + unsigned int rv, len, left, crc; + PHMD5 hash_ctx; + + // ファイルの開始位置を offsetバイト目にする + if (!SetFilePointerEx(hFileRead, *((PLARGE_INTEGER)&offset), NULL, FILE_BEGIN)) + return 1; + + crc = 0xFFFFFFFF; // 初期化 + Phmd5Begin(&hash_ctx); + + left = avail_size; // 実際に読み込むサイズ + while (left > 0){ + len = IO_SIZE; + if (left < IO_SIZE) + len = left; + if (!ReadFile(hFileRead, buf, len, &rv, NULL)){ + print_win32_err(); // エラー通知 + return 2; + } else if (len != rv){ + return 3; + } + left -= len; + + crc = crc_update(crc, buf, len); // CRC-32 計算 + Phmd5Process(&hash_ctx, buf, len); // MD5 計算 + } + + if (avail_size < block_size){ // ブロック・サイズまで 0で埋めて計算する + crc = crc_update_zero(crc, block_size - avail_size); + Phmd5ProcessZero(&hash_ctx, block_size - avail_size); + } + + crc ^= 0xFFFFFFFF; // 最終処理 + memcpy(hash + 16, &crc, 4); + Phmd5End(&hash_ctx); + memcpy(hash, hash_ctx.hash, 16); + + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +//#define TIMER // 実験用 + +#ifdef TIMER +static unsigned int time_start, time1_start; +static unsigned int time_total = 0, time2_total = 0, time3_total = 0; +#endif + +// ファイルのハッシュ値と各スライスのチェックサムを同時に計算する +int file_hash_crc( + wchar_t *file_name, // ハッシュ値を求めるファイル + __int64 file_left, + unsigned char *hash, // ハッシュ値 (16バイト) + unsigned char *sum, // チェックサム (MD5 + CRC-32) の配列 + unsigned int *time_last, // 前回に経過表示した時刻 + __int64 *prog_now) // 経過表示での現在位置 +{ + unsigned char *buf; + __declspec( align(64) ) unsigned char buf1[IO_SIZE * 2]; + wchar_t file_path[MAX_LEN]; + unsigned int err = 0, len, off, crc, block_left = 0, read_size; + __int64 file_off; + PHMD5 hash_ctx, block_ctx; + HANDLE hFile; + OVERLAPPED ol; +#ifdef TIMER +time1_start = GetTickCount(); +#endif + + // ソース・ファイルを開く + wcscpy(file_path, base_dir); + wcscpy(file_path + base_len, file_name); + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + return 1; + } + + // 非同期ファイル・アクセスの準備をする + memset(&ol, 0, sizeof(OVERLAPPED)); + ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (ol.hEvent == NULL){ + err = 1; + goto error_end; + } + file_off = IO_SIZE; + buf = buf1 + IO_SIZE; + + // 最初の分を読み込む + read_size = IO_SIZE; + if (file_left < IO_SIZE) + read_size = (unsigned int)file_left; +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = ReadFile(hFile, buf1, read_size, NULL, &ol); +#ifdef TIMER +time2_total += GetTickCount() - time_start; +#endif + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + err = 1; + goto error_end; + } + Phmd5Begin(&hash_ctx); // ファイルの MD5 計算を開始する + + while (file_left > 0){ + len = read_size; + file_left -= read_size; + (*prog_now) += read_size; + + // 前回の読み込みが終わるのを待つ + WaitForSingleObject(ol.hEvent, INFINITE); + + // 次の分を読み込み開始しておく + if (file_left > 0){ + read_size = IO_SIZE; + if (file_left < IO_SIZE) + read_size = (unsigned int)file_left; + ol.Offset = (unsigned int)file_off; + ol.OffsetHigh = (unsigned int)(file_off >> 32); + file_off += IO_SIZE; +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = ReadFile(hFile, buf, read_size, NULL, &ol); +#ifdef TIMER +time2_total += GetTickCount() - time_start; +#endif + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + err = 1; + goto error_end; + } + } + // バッファーを入れ替える + if (buf == buf1){ + buf = buf1 + IO_SIZE; + } else { + buf = buf1; + } + +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = 0; // チェックサム計算 + if (block_left > 0){ // 前回足りなかった分を追加する + //printf("file_left = %I64d, block_left = %d\n", file_left, block_left); + if (block_left <= len){ + crc = crc_update(crc, buf, block_left) ^ 0xFFFFFFFF; // CRC-32 計算 + Phmd5Process2(&hash_ctx, &block_ctx, buf, block_left); // MD5 計算 + Phmd5End(&block_ctx); // 最終処理 + memcpy(sum, block_ctx.hash, 16); + memcpy(sum + 16, &crc, 4); + sum += 20; + off += block_left; + block_left = 0; + } else { + crc = crc_update(crc, buf, len); // CRC-32 計算 + Phmd5Process2(&hash_ctx, &block_ctx, buf, len); // MD5 計算 + off = len; + block_left -= len; + } + } + for (; off < len; off += block_size){ + Phmd5Begin(&block_ctx); + if (off + block_size <= len){ + crc = crc_update(0xFFFFFFFF, buf + off, block_size) ^ 0xFFFFFFFF; // CRC-32 計算 + Phmd5Process2(&hash_ctx, &block_ctx, buf + off, block_size); // MD5 計算 + Phmd5End(&block_ctx); // 最終処理 + memcpy(sum, block_ctx.hash, 16); + memcpy(sum + 16, &crc, 4); + sum += 20; + } else { // スライスが途中までなら + crc = crc_update(0xFFFFFFFF, buf + off, len - off); // CRC-32 計算 + Phmd5Process2(&hash_ctx, &block_ctx, buf + off, len - off); // MD5 計算 + block_left = block_size - len + off; + } + } +#ifdef TIMER +time3_total += GetTickCount() - time_start; +#endif + + // 経過表示 + if (GetTickCount() - (*time_last) >= UPDATE_TIME){ + if (print_progress((int)(((*prog_now) * 1000) / total_file_size))){ + err = 2; + goto error_end; + } + (*time_last) = GetTickCount(); + } + } + + // 最終ブロックが半端なら + if (block_left > 0){ // 残りを 0 でパディングする (PAR2 仕様の欠点) + crc = crc_update_zero(crc, block_left) ^ 0xFFFFFFFF; // CRC-32 計算 + Phmd5ProcessZero(&block_ctx, block_left); + Phmd5End(&block_ctx); + memcpy(sum, block_ctx.hash, 16); + memcpy(sum + 16, &crc, 4); + } + Phmd5End(&hash_ctx); // 最終処理 + memcpy(hash, hash_ctx.hash, 16); + +error_end: + CancelIo(hFile); // 非同期 IO を取り消す + CloseHandle(hFile); + if (ol.hEvent) + CloseHandle(ol.hEvent); + +#ifdef TIMER +time_total += GetTickCount() - time1_start; +if (*prog_now == total_file_size){ + printf("\nread %d.%03d sec\n", time2_total / 1000, time2_total % 1000); + printf("main %d.%03d sec\n", time3_total / 1000, time3_total % 1000); + if (time_total > 0){ + time_start = (int)((total_file_size * 125) / ((__int64)time_total * 131072)); + } else { + time_start = 0; + } + printf("total %d.%03d sec, %d MB/s\n", time_total / 1000, time_total % 1000, time_start); +} +#endif + return err; +} + +/* USBが切断されても再接続を待つ実験 +int file_hash_crc( + wchar_t *file_name, // ハッシュ値を求めるファイル + __int64 file_left, + unsigned char *hash, // ハッシュ値 (16バイト) + unsigned char *sum, // チェックサム (MD5 + CRC-32) の配列 + unsigned int *time_last, // 前回に経過表示した時刻 + __int64 *prog_now) // 経過表示での現在位置 +{ + unsigned char *buf, *buf2; + __declspec( align(64) ) unsigned char buf1[IO_SIZE * 2]; + wchar_t file_path[MAX_LEN]; + unsigned int err = 0, err_last = 0, err_count = 0; + unsigned int len, off, crc, block_left = 0, read_size; + __int64 file_off; + PHMD5 hash_ctx, block_ctx; + HANDLE hFile; + OVERLAPPED ol; +#ifdef TIMER +time1_start = GetTickCount(); +#endif + + // ソース・ファイルを開く + wcscpy(file_path, base_dir); + wcscpy(file_path + base_len, file_name); + //printf_cp("\nfile = %s\n", file_path); +error_retry_read: + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + err = GetLastError(); + if ((err_count <= 30) && ((err == ERROR_PATH_NOT_FOUND) || (err == ERROR_NOT_READY))){ // 3 or 21 + if (error_progress(err, err_last) == 2) + return 2; + err_last = err; + err_count++; + err = 0; + goto error_retry_read; // ファイルを開きなおす + } else { + print_win32_err(); + return 1; + } + } + + // 非同期ファイル・アクセスの準備をする + memset(&ol, 0, sizeof(OVERLAPPED)); + ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (ol.hEvent == NULL){ + err = 1; + goto error_end; + } + file_off = IO_SIZE; + buf = buf1 + IO_SIZE; + + // 最初の分を読み込む + read_size = IO_SIZE; + if (file_left < IO_SIZE) + read_size = (unsigned int)file_left; +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = ReadFile(hFile, buf1, read_size, NULL, &ol); +#ifdef TIMER +time2_total += GetTickCount() - time_start; +#endif + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + err = 1; + goto error_end; + } + Phmd5Begin(&hash_ctx); // ファイルの MD5 計算を開始する + + while (file_left > 0){ + len = read_size; + file_left -= read_size; + (*prog_now) += read_size; + + // 前回の読み込みが終わるのを待つ +// WaitForSingleObject(ol.hEvent, INFINITE); + +//Sleep(200); + +error_retry_wait: + // 前回の読み込みが終わるのを待つ、エラーも取得できる + if (GetOverlappedResult(hFile, &ol, &off, TRUE) == 0){ + err = GetLastError(); + if ((err_count <= 30) && (err == ERROR_NOT_READY)){ // 21, The device is not ready. + CloseHandle(hFile); // 一度ファイルを閉じる + file_off -= IO_SIZE; // 前回の読み取り位置に戻す + ol.Offset = (unsigned int)file_off; + ol.OffsetHigh = (unsigned int)(file_off >> 32); + +error_retry_pause: + if (error_progress(err, err_last) == 2){ + err = 2; + goto error_end; + } + err_last = err; + err_count++; + err = 0; + // ファイルを開きなおす + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + err = GetLastError(); + if ((err_count <= 30) && ((err == ERROR_NOT_READY) || (err == ERROR_PATH_NOT_FOUND))) // 21 or 3 + goto error_retry_pause; + print_win32_err(); + err = 1; + goto error_end; + } + // 再度読み込む + if (buf == buf1){ + buf2 = buf1 + IO_SIZE; + } else { + buf2 = buf1; + } + off = ReadFile(hFile, buf2, read_size, NULL, &ol); + if (off == 0){ + err = GetLastError(); + if (err != ERROR_IO_PENDING){ + if ((err_count <= 30) && (err == ERROR_NOT_READY)){ + CancelIo(hFile); // 非同期 IO を取り消す + CloseHandle(hFile); // 一度ファイルを閉じる + goto error_retry_pause; + } + print_win32_err(); + err = 1; + goto error_end; + } + err = 0; + } + file_off += IO_SIZE; // 読み取り位置を次の位置にする + goto error_retry_wait; + + } else { // その他のエラー + print_win32_err(); + printf("GetOverlappedResult: file_off = %I64d, file_left = %I64d\n", file_off, file_left); + err = 1; + goto error_end; + } + } + err_last = 0; + err_count = 0; + + // 次の分を読み込み開始しておく + if (file_left > 0){ + read_size = IO_SIZE; + if (file_left < IO_SIZE) + read_size = (unsigned int)file_left; + ol.Offset = (unsigned int)file_off; + ol.OffsetHigh = (unsigned int)(file_off >> 32); + file_off += IO_SIZE; +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = ReadFile(hFile, buf, read_size, NULL, &ol); +#ifdef TIMER +time2_total += GetTickCount() - time_start; +#endif + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + printf("ReadFile: file_off = %I64d, file_left = %I64d\n", file_off, file_left); + err = 1; + goto error_end; + } + } + // バッファーを入れ替える + if (buf == buf1){ + buf = buf1 + IO_SIZE; + } else { + buf = buf1; + } + +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = 0; // チェックサム計算 + if (block_left > 0){ // 前回足りなかった分を追加する + //printf("file_left = %I64d, block_left = %d\n", file_left, block_left); + if (block_left <= len){ + crc = crc_update(crc, buf, block_left) ^ 0xFFFFFFFF; // CRC-32 計算 + Phmd5Process2(&hash_ctx, &block_ctx, buf, block_left); // MD5 計算 + Phmd5End(&block_ctx); // 最終処理 + memcpy(sum, block_ctx.hash, 16); + memcpy(sum + 16, &crc, 4); + sum += 20; + off += block_left; + block_left = 0; + } else { + crc = crc_update(crc, buf, len); // CRC-32 計算 + Phmd5Process2(&hash_ctx, &block_ctx, buf, len); // MD5 計算 + off = len; + block_left -= len; + } + } + for (; off < len; off += block_size){ + Phmd5Begin(&block_ctx); + if (off + block_size <= len){ + crc = crc_update(0xFFFFFFFF, buf + off, block_size) ^ 0xFFFFFFFF; // CRC-32 計算 + Phmd5Process2(&hash_ctx, &block_ctx, buf + off, block_size); // MD5 計算 + Phmd5End(&block_ctx); // 最終処理 + memcpy(sum, block_ctx.hash, 16); + memcpy(sum + 16, &crc, 4); + sum += 20; + } else { // スライスが途中までなら + crc = crc_update(0xFFFFFFFF, buf + off, len - off); // CRC-32 計算 + Phmd5Process2(&hash_ctx, &block_ctx, buf + off, len - off); // MD5 計算 + block_left = block_size - len + off; + } + } +#ifdef TIMER +time3_total += GetTickCount() - time_start; +#endif + + // 経過表示 + if (GetTickCount() - (*time_last) >= UPDATE_TIME){ + if (print_progress((int)(((*prog_now) * 1000) / total_file_size))){ + err = 2; + goto error_end; + } + (*time_last) = GetTickCount(); + } + } + + // 最終ブロックが半端なら + if (block_left > 0){ // 残りを 0 でパディングする (PAR2 仕様の欠点) + crc = crc_update_zero(crc, block_left) ^ 0xFFFFFFFF; // CRC-32 計算 + Phmd5ProcessZero(&block_ctx, block_left); + Phmd5End(&block_ctx); + memcpy(sum, block_ctx.hash, 16); + memcpy(sum + 16, &crc, 4); + } + Phmd5End(&hash_ctx); // 最終処理 + memcpy(hash, hash_ctx.hash, 16); + +error_end: + CancelIo(hFile); // 非同期 IO を取り消す + CloseHandle(hFile); + if (ol.hEvent) + CloseHandle(ol.hEvent); + +#ifdef TIMER +time_total += GetTickCount() - time1_start; +if (*prog_now == total_file_size){ + printf("\nread %d.%03d sec\n", time2_total / 1000, time2_total % 1000); + printf("main %d.%03d sec\n", time3_total / 1000, time3_total % 1000); + if (time_total > 0){ + time_start = (int)((total_file_size * 125) / ((__int64)time_total * 131072)); + } else { + time_start = 0; + } + printf("total %d.%03d sec, %d MB/s\n", time_total / 1000, time_total % 1000, time_start); +} +#endif + return err; +} +*/ + +/* +// ヒープ領域を使うバージョン +// ファイルのハッシュ値と各スライスのチェックサムを同時に計算する +int file_hash_crc( + wchar_t *file_name, // ハッシュ値を求めるファイル + __int64 file_left, + unsigned char *hash, // ハッシュ値 (16バイト) + unsigned char *sum, // チェックサム (MD5 + CRC-32) の配列 + unsigned int *time_last, // 前回に経過表示した時刻 + __int64 *prog_now) // 経過表示での現在位置 +{ + unsigned char *buf, *buf1; + wchar_t file_path[MAX_LEN]; + unsigned int err = 0, len, off, crc, block_left = 0, read_size, io_size; + __int64 file_off; + PHMD5 hash_ctx, block_ctx; + HANDLE hFile; + OVERLAPPED ol; +#ifdef TIMER +time1_start = GetTickCount(); +#endif + + // ソース・ファイルを開く + wcscpy(file_path, base_dir); + wcscpy(file_path + base_len, file_name); + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + return 1; + } + + // バッファー・サイズが大きいのでヒープ領域を使う + for (io_size = IO_SIZE; io_size < 1048576; io_size += IO_SIZE){ // 1 MB までにする + if ((io_size + IO_SIZE > (cpu_cache << 17)) || ((__int64)(io_size + IO_SIZE) * 4 > file_left)) + break; + } + buf1 = _aligned_malloc(io_size * 2, 64); + if (buf1 == NULL){ + printf("malloc, %d\n", io_size * 2); + CloseHandle(hFile); + return 1; + } + buf = buf1 + io_size; + + // 非同期ファイル・アクセスの準備をする + memset(&ol, 0, sizeof(OVERLAPPED)); + ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (ol.hEvent == NULL){ + err = 1; + goto error_end; + } + file_off = io_size; + + // 最初の分を読み込む + read_size = io_size; + if (file_left < io_size) + read_size = (unsigned int)file_left; +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = ReadFile(hFile, buf1, read_size, NULL, &ol); +#ifdef TIMER +time2_total += GetTickCount() - time_start; +#endif + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + err = 1; + goto error_end; + } + Phmd5Begin(&hash_ctx); // ファイルの MD5 計算を開始する + + while (file_left > 0){ + len = read_size; + file_left -= read_size; + (*prog_now) += read_size; + + // 前回の読み込みが終わるのを待つ + WaitForSingleObject(ol.hEvent, INFINITE); + + // 次の分を読み込み開始しておく + if (file_left > 0){ + read_size = io_size; + if (file_left < io_size) + read_size = (unsigned int)file_left; + ol.Offset = (unsigned int)file_off; + ol.OffsetHigh = (unsigned int)(file_off >> 32); + file_off += io_size; +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = ReadFile(hFile, buf, read_size, NULL, &ol); +#ifdef TIMER +time2_total += GetTickCount() - time_start; +#endif + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + err = 1; + goto error_end; + } + } + // バッファーを入れ替える + if (buf == buf1){ + buf = buf1 + io_size; + } else { + buf = buf1; + } + +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = 0; // チェックサム計算 + if (block_left > 0){ // 前回足りなかった分を追加する + //printf("file_left = %I64d, block_left = %d\n", file_left, block_left); + if (block_left <= len){ + crc = crc_update(crc, buf, block_left) ^ 0xFFFFFFFF; // CRC-32 計算 + Phmd5Process2(&hash_ctx, &block_ctx, buf, block_left); // MD5 計算 + Phmd5End(&block_ctx); // 最終処理 + memcpy(sum, block_ctx.hash, 16); + memcpy(sum + 16, &crc, 4); + sum += 20; + off += block_left; + block_left = 0; + } else { + crc = crc_update(crc, buf, len); // CRC-32 計算 + Phmd5Process2(&hash_ctx, &block_ctx, buf, len); // MD5 計算 + off = len; + block_left -= len; + } + } + for (; off < len; off += block_size){ + Phmd5Begin(&block_ctx); + if (off + block_size <= len){ + crc = crc_update(0xFFFFFFFF, buf + off, block_size) ^ 0xFFFFFFFF; // CRC-32 計算 + Phmd5Process2(&hash_ctx, &block_ctx, buf + off, block_size); // MD5 計算 + Phmd5End(&block_ctx); // 最終処理 + memcpy(sum, block_ctx.hash, 16); + memcpy(sum + 16, &crc, 4); + sum += 20; + } else { // スライスが途中までなら + crc = crc_update(0xFFFFFFFF, buf + off, len - off); // CRC-32 計算 + Phmd5Process2(&hash_ctx, &block_ctx, buf + off, len - off); // MD5 計算 + block_left = block_size - len + off; + } + } +#ifdef TIMER +time3_total += GetTickCount() - time_start; +#endif + + // 経過表示 + if (GetTickCount() - (*time_last) >= UPDATE_TIME){ + if (print_progress((int)(((*prog_now) * 1000) / total_file_size))){ + err = 2; + goto error_end; + } + (*time_last) = GetTickCount(); + } + } + + // 最終ブロックが半端なら + if (block_left > 0){ // 残りを 0 でパディングする (PAR2 仕様の欠点) + crc = crc_update_zero(crc, block_left) ^ 0xFFFFFFFF; // CRC-32 計算 + Phmd5ProcessZero(&block_ctx, block_left); + Phmd5End(&block_ctx); + memcpy(sum, block_ctx.hash, 16); + memcpy(sum + 16, &crc, 4); + } + Phmd5End(&hash_ctx); // 最終処理 + memcpy(hash, hash_ctx.hash, 16); + +error_end: + CancelIo(hFile); // 非同期 IO を取り消す + CloseHandle(hFile); + if (ol.hEvent) + CloseHandle(ol.hEvent); + if (buf1) + _aligned_free(buf1); + +#ifdef TIMER +time_total += GetTickCount() - time1_start; +if (*prog_now == total_file_size){ + printf("\nread %d.%03d sec\n", time2_total / 1000, time2_total % 1000); + printf("main %d.%03d sec\n", time3_total / 1000, time3_total % 1000); + if (time_total > 0){ + time_start = (int)((total_file_size * 125) / ((__int64)time_total * 131072)); + } else { + time_start = 0; + } + printf("total %d.%03d sec, %d MB/s\n", time_total / 1000, time_total % 1000, time_start); +} +#endif + return err; +} +*/ + +// 複数ファイルのハッシュ値を同時に計算する +DWORD WINAPI file_hash_crc2(LPVOID lpParameter) +{ + unsigned char *buf, *buf1, *hash, *sum; + wchar_t file_path[MAX_LEN]; + int prog_loop, prog_tick, prog_rv; + unsigned int err = 0, len, off, crc, block_left = 0, read_size, io_size; + unsigned int time_last; + __int64 file_left, file_off; + PHMD5 hash_ctx, block_ctx; + HANDLE hFile; + OVERLAPPED ol; + FILE_HASH_TH *file_th; + + file_th = (FILE_HASH_TH *)lpParameter; + wcscpy(file_path, base_dir); + wcscpy(file_path + base_len, file_th->file_name); + file_left = file_th->file_size; + hash = file_th->hash; // ハッシュ値 (16バイト) + sum = file_th->sum; // チェックサム (MD5 + CRC-32) の配列 + prog_loop = 0; + + // ソース・ファイルを開く + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL); + // アクセス・モードで違いが出るかも? + //hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + return 1; + } + + // バッファー・サイズが大きいのでヒープ領域を使う + prog_tick = 1; + for (io_size = IO_SIZE; io_size < 1048576; io_size += IO_SIZE){ // IO_SIZE の倍数で 1 MB までにする + if ((io_size + IO_SIZE > (cpu_cache << 17)) || ((__int64)(io_size + IO_SIZE) * 4 > file_left)) + break; + prog_tick++; + } + //printf("\n io_size = %d, prog_tick = %d\n", io_size, prog_tick); + buf1 = _aligned_malloc(io_size * 2, 64); + if (buf1 == NULL){ + printf("malloc, %d\n", io_size * 2); + err = 1; + goto error_end; + } + buf = buf1 + io_size; + + // 非同期ファイル・アクセスの準備をする + memset(&ol, 0, sizeof(OVERLAPPED)); + ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (ol.hEvent == NULL){ + err = 1; + goto error_end; + } + file_off = io_size; + + // 最初の分を読み込む + read_size = io_size; + if (file_left < io_size) + read_size = (unsigned int)file_left; + off = ReadFile(hFile, buf1, read_size, NULL, &ol); + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + err = 1; + goto error_end; + } + Phmd5Begin(&hash_ctx); // ファイルの MD5 計算を開始する + + time_last = GetTickCount(); + while (file_left > 0){ + len = read_size; + file_left -= read_size; + + // 前回の読み込みが終わるのを待つ + WaitForSingleObject(ol.hEvent, INFINITE); + + // 次の分を読み込み開始しておく + if (file_left > 0){ + read_size = io_size; + if (file_left < io_size) + read_size = (unsigned int)file_left; + ol.Offset = (unsigned int)file_off; + ol.OffsetHigh = (unsigned int)(file_off >> 32); + file_off += io_size; + off = ReadFile(hFile, buf, read_size, NULL, &ol); + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + err = 1; + goto error_end; + } + } + // バッファーを入れ替える + if (buf == buf1){ + buf = buf1 + io_size; + } else { + buf = buf1; + } + + off = 0; // チェックサム計算 + if (block_left > 0){ // 前回足りなかった分を追加する + //printf("file_left = %I64d, block_left = %d\n", file_left, block_left); + if (block_left <= len){ + crc = crc_update(crc, buf, block_left) ^ 0xFFFFFFFF; // CRC-32 計算 + //Phmd5Process(&block_ctx, buf, block_left); + Phmd5Process2(&hash_ctx, &block_ctx, buf, block_left); // MD5 計算 + Phmd5End(&block_ctx); // 最終処理 + memcpy(sum, block_ctx.hash, 16); + memcpy(sum + 16, &crc, 4); + sum += 20; + off += block_left; + block_left = 0; + } else { + crc = crc_update(crc, buf, len); // CRC-32 計算 + //Phmd5Process(&block_ctx, buf, len); + Phmd5Process2(&hash_ctx, &block_ctx, buf, len); // MD5 計算 + off = len; + block_left -= len; + } + } + for (; off < len; off += block_size){ + Phmd5Begin(&block_ctx); + if (off + block_size <= len){ + crc = crc_update(0xFFFFFFFF, buf + off, block_size) ^ 0xFFFFFFFF; // CRC-32 計算 + //Phmd5Process(&block_ctx, buf + off, block_size); + Phmd5Process2(&hash_ctx, &block_ctx, buf + off, block_size); // MD5 計算 + Phmd5End(&block_ctx); // 最終処理 + memcpy(sum, block_ctx.hash, 16); + memcpy(sum + 16, &crc, 4); + sum += 20; + } else { // スライスが途中までなら + crc = crc_update(0xFFFFFFFF, buf + off, len - off); // CRC-32 計算 + //Phmd5Process(&block_ctx, buf + off, len - off); + Phmd5Process2(&hash_ctx, &block_ctx, buf + off, len - off); // MD5 計算 + block_left = block_size - len + off; + } + } + + // 経過表示のために進捗状況を更新する + if (GetTickCount() - time_last >= UPDATE_TIME / 2){ + prog_rv = InterlockedExchange(&(file_th->loop), prog_loop); + if (prog_rv == -1){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + prog_loop += prog_tick; + } + + // 最終ブロックが半端なら + if (block_left > 0){ // 残りを 0 でパディングする (PAR2 仕様の欠点) + crc = crc_update_zero(crc, block_left) ^ 0xFFFFFFFF; // CRC-32 計算 + Phmd5ProcessZero(&block_ctx, block_left); + Phmd5End(&block_ctx); + memcpy(sum, block_ctx.hash, 16); + memcpy(sum + 16, &crc, 4); + sum += 20; + } + Phmd5End(&hash_ctx); // 最終処理 + memcpy(hash, hash_ctx.hash, 16); + + // サブ・スレッド側でパケット2個を完成させる + data_md5(hash - 48, (int)(file_th->sum - hash) - 32, hash - 64); // File Description packet の MD5 + data_md5(file_th->sum - 48, 32 + (int)(sum - file_th->sum) + 16, file_th->sum - 64); // Input File Slice Checksum packet の MD5 + + // ファイルの MD5-16k はもう不要なので、ブロックの CRC-32 に変更する + len = (int)((file_th->file_size + (__int64)block_size - 1) / block_size); // ブロック数 + memset(file_th->crc, 0, 16); + for (off = 0; off < len; off++){ // XOR して 16バイトに減らす + memcpy(&crc, file_th->sum + (20 * off + 16), 4); + (file_th->crc)[off & 3] ^= crc; + } + +error_end: + CancelIo(hFile); // 非同期 IO を取り消す + CloseHandle(hFile); + if (ol.hEvent) + CloseHandle(ol.hEvent); + if (buf1) + _aligned_free(buf1); + + return err; +} + +// ファイルのハッシュ値が同じか調べる (全てのスライスのチェックサムも) +// 0~=破損してるが何個目まで同じ, -1=MD5-16kが異なる, -2=キャンセル, -3=同じ(完全か追加) +int file_hash_check( + int num, // file_ctx におけるファイル番号 + wchar_t *file_name, // 表示するファイル名 + HANDLE hFile, // ファイルのハンドル + int prog_min, // 検出数がこの値を超えたら合計数を表示する (INT_MAX なら表示しない) + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 (NULL なら比較しない) +{ + unsigned char *buf; + __declspec( align(64) ) unsigned char buf1[IO_SIZE * 2]; + int find_next, comp_num = 0; + unsigned int len, off, crc, block_left = 0, read_size; + unsigned int time_last; + __int64 file_size, file_left, file_off; + PHMD5 hash_ctx, block_ctx; + OVERLAPPED ol; +#ifdef TIMER +time1_start = GetTickCount(); +#endif + + prog_last = -1; // 検証中のファイル名を毎回表示する + time_last = GetTickCount(); + file_size = files[num].size; // 本来のファイル・サイズ + find_next = files[num].b_off; // 先頭ブロックの番号 + + // 非同期ファイル・アクセスの準備をする + memset(&ol, 0, sizeof(OVERLAPPED)); + ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (ol.hEvent == NULL) + return -1; + buf = buf1 + IO_SIZE; + + // まずは先頭 16KB のハッシュ値を比較する + len = 16384; + if (file_size < 16384){ + len = (unsigned int)file_size; + file_left = 0; + } else { + file_left = file_size - 16384; // 本来のファイル・サイズまでしか検査しない + } +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = ReadFile(hFile, buf, len, NULL, &ol); +#ifdef TIMER +time2_total += GetTickCount() - time_start; +#endif + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + comp_num = -1; + goto error_end; + } + WaitForSingleObject(ol.hEvent, INFINITE); + + Phmd5Begin(&hash_ctx); // 初期化 + Phmd5Process(&hash_ctx, buf, len); // MD5 計算 + Phmd5End(&hash_ctx); // 最終処理 + if (file_left == 0){ // ファイルは 16KB 以下 + if (s_blk != NULL){ // 全てのスライスを比較する + for (off = 0; off < len; off += block_size){ + Phmd5Begin(&block_ctx); + if (off + block_size <= len){ + crc = crc_update(0, buf + off, block_size); + Phmd5Process(&block_ctx, buf + off, block_size); + } else { // 末尾の半端なスライスなら + crc = crc_update(0, buf + off, len - off); + Phmd5Process(&block_ctx, buf + off, len - off); + block_left = block_size - len + off; // ブロック・サイズまで 0で埋めて計算する + crc = crc_update_zero(crc, block_left); + Phmd5ProcessZero(&block_ctx, block_left); + } + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + find_next++; + } + } + if (memcmp(hash_ctx.hash, files[num].hash + 16, 16) != 0){ // 16k MD5 が異なる + comp_num = -1; + goto error_end; + } + comp_num = -3; // 同じ + goto error_end; + } + // 16KB よりも大きくて 16k MD5 が異なる場合は、スライス単位の検査をしない + if (memcmp(hash_ctx.hash, files[num].hash + 16, 16) != 0){ + comp_num = -1; + goto error_end; + } + + // 16k MD5 が一致したら残りの部分も計算する + Phmd5Begin(&hash_ctx); // 初期化 + Phmd5Process(&hash_ctx, buf, 16384); // MD5 計算 + if (s_blk != NULL){ + for (off = 0; off < 16384; off += block_size){ + Phmd5Begin(&block_ctx); + if (off + block_size <= 16384){ + crc = crc_update(0, buf + off, block_size); + Phmd5Process(&block_ctx, buf + off, block_size); + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + find_next++; + } else { // スライスが途中までなら + crc = crc_update(0, buf + off, 16384 - off); + Phmd5Process(&block_ctx, buf + off, 16384 - off); + block_left = block_size - 16384 + off; + } + } + } + + // 次の分を読み込む + ol.Offset = 16384; + file_off = 16384 + IO_SIZE; + read_size = IO_SIZE; + if (file_left < IO_SIZE) + read_size = (unsigned int)file_left; +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = ReadFile(hFile, buf1, read_size, NULL, &ol); +#ifdef TIMER +time2_total += GetTickCount() - time_start; +#endif + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + goto error_end; // 読み取りが失敗した所で終わる + } + + while (file_left > 0){ + len = read_size; // 読み込んだサイズ + file_left -= read_size; + + // 前回の読み込みが終わるのを待つ + WaitForSingleObject(ol.hEvent, INFINITE); + + // 次の分を読み込み開始しておく + if (file_left > 0){ + read_size = IO_SIZE; + if (file_left < IO_SIZE) + read_size = (unsigned int)file_left; + ol.Offset = (unsigned int)file_off; + ol.OffsetHigh = (unsigned int)(file_off >> 32); + file_off += IO_SIZE; +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = ReadFile(hFile, buf, read_size, NULL, &ol); +#ifdef TIMER +time2_total += GetTickCount() - time_start; +#endif + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + goto error_end; // 読み取りが失敗した所で終わる + } + } + // バッファーを入れ替える + if (buf == buf1){ + buf = buf1 + IO_SIZE; + } else { + buf = buf1; + } + +#ifdef TIMER +time_start = GetTickCount(); +#endif + if (s_blk != NULL){ + off = 0; + if (block_left > 0){ // 前回足りなかった分を追加する + if (block_left <= len){ + crc = crc_update(crc, buf, block_left); + Phmd5Process2(&hash_ctx, &block_ctx, buf, block_left); + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + find_next++; + off = block_left; + block_left = 0; + } else { + crc = crc_update(crc, buf, len); + Phmd5Process2(&hash_ctx, &block_ctx, buf, len); + off = len; + block_left -= len; + } + } + for (; off < len; off += block_size){ + Phmd5Begin(&block_ctx); + if (off + block_size <= len){ + // s_blk[].crc は初期値と最終処理の 0xFFFFFFFF を取り除いてる + crc = crc_update(0, buf + off, block_size); + Phmd5Process2(&hash_ctx, &block_ctx, buf + off, block_size); + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + find_next++; + } else { // スライスが途中までなら + crc = crc_update(0, buf + off, len - off); + Phmd5Process2(&hash_ctx, &block_ctx, buf + off, len - off); + block_left = block_size - len + off; + } + } + } else { + Phmd5Process(&hash_ctx, buf, len); // MD5 計算 + } +#ifdef TIMER +time3_total += GetTickCount() - time_start; +#endif + + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (comp_num <= prog_min){ // 最低値以下なら検出スライス数の合計は表示しない + len = -1; + } else { + len = first_num + comp_num - prog_min; + } + if (print_progress_file((int)(((file_size - file_left) * 1000) / file_size), len, file_name)){ + comp_num = -2; + goto error_end; + } + time_last = GetTickCount(); + } + } + Phmd5End(&hash_ctx); // 最終処理 + + if ((s_blk != NULL) && (block_left > 0)){ // 末尾の半端なスライスがあるなら + crc = crc_update_zero(crc, block_left); + Phmd5ProcessZero(&block_ctx, block_left); + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + } + + if (memcmp(hash_ctx.hash, files[num].hash, 16) == 0) // ファイル全域の MD5 が同じ + comp_num = -3; + +error_end: + CancelIo(hFile); // 非同期 IO を取り消す + if (ol.hEvent) + CloseHandle(ol.hEvent); + +#ifdef TIMER +time_total += GetTickCount() - time1_start; + printf("\nread %d.%03d sec\n", time2_total / 1000, time2_total % 1000); + printf("main %d.%03d sec\n", time3_total / 1000, time3_total % 1000); + if (time_total > 0){ + time_start = (int)((file_size * 125) / ((__int64)time_total * 131072)); + } else { + time_start = 0; + } + printf("total %d.%03d sec, %d MB/s\n", time_total / 1000, time_total % 1000, time_start); +#endif + return comp_num; +} + +// バックグラウンドでファイルのハッシュ値が同じか調べる (全てのスライスのチェックサムも) +// comp_num 0~=破損してるが何個目まで同じ, -1=MD5-16kが異なる, -3=同じ(完全か追加) +DWORD WINAPI file_hash_background(LPVOID lpParameter) +{ + unsigned char *buf, *buf1; + int num, find_next, comp_num = 0; + unsigned int len, off, crc, block_left = 0, read_size, io_size; + unsigned int time_last; + __int64 file_size, file_left, file_off; + file_ctx_r *files; + source_ctx_r *s_blk; + HANDLE hFile; + PHMD5 hash_ctx, block_ctx; + OVERLAPPED ol; + FILE_CHECK_TH *file_th; + + file_th = (FILE_CHECK_TH *)lpParameter; + num = file_th->num; // file_ctx におけるファイル番号 + hFile = file_th->hFile; // ファイルのハンドル + files = file_th->files; // 各ソース・ファイルの情報 + s_blk = file_th->s_blk; // 各ソース・ブロックの情報 (NULL なら比較しない) + file_size = files[num].size; // 本来のファイル・サイズ + find_next = files[num].b_off; // 先頭ブロックの番号 + + // バッファー・サイズが大きいのでヒープ領域を使う + for (io_size = IO_SIZE; io_size < 1048576; io_size += IO_SIZE){ // IO_SIZE の倍数で 1 MB までにする + if ((io_size + IO_SIZE > (cpu_cache << 17)) || ((__int64)(io_size + IO_SIZE) * 4 > file_size)) + break; + } + //printf("\n io_size = %d\n", io_size); + buf1 = _aligned_malloc(io_size * 2, 64); + if (buf1 == NULL){ + printf("malloc, %d\n", io_size * 2); + comp_num = -1; + goto error_end; + } + buf = buf1 + io_size; + + // 非同期ファイル・アクセスの準備をする + memset(&ol, 0, sizeof(OVERLAPPED)); + ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (ol.hEvent == NULL){ + comp_num = -1; + goto error_end; + } + time_last = GetTickCount(); + + // まずは先頭 16KB のハッシュ値を比較する + len = 16384; + if (file_size < 16384){ + len = (unsigned int)file_size; + file_left = 0; + } else { + file_left = file_size - 16384; // 本来のファイル・サイズまでしか検査しない + } + off = ReadFile(hFile, buf, len, NULL, &ol); + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + comp_num = -1; + goto error_end; + } + WaitForSingleObject(ol.hEvent, INFINITE); + + Phmd5Begin(&hash_ctx); // 初期化 + Phmd5Process(&hash_ctx, buf, len); // MD5 計算 + Phmd5End(&hash_ctx); // 最終処理 + if (file_left == 0){ // ファイルは 16KB 以下 + if (s_blk != NULL){ // 全てのスライスを比較する + for (off = 0; off < len; off += block_size){ + Phmd5Begin(&block_ctx); + if (off + block_size <= len){ + crc = crc_update(0, buf + off, block_size); + Phmd5Process(&block_ctx, buf + off, block_size); + } else { // 末尾の半端なスライスなら + crc = crc_update(0, buf + off, len - off); + Phmd5Process(&block_ctx, buf + off, len - off); + block_left = block_size - len + off; // ブロック・サイズまで 0で埋めて計算する + crc = crc_update_zero(crc, block_left); + Phmd5ProcessZero(&block_ctx, block_left); + } + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + find_next++; + } + } + if (memcmp(hash_ctx.hash, files[num].hash + 16, 16) != 0){ // 16k MD5 が異なる + comp_num = -1; + goto error_end; + } + comp_num = -3; // 同じ + goto error_end; + } + // 16KB よりも大きくて 16k MD5 が異なる場合は、スライス単位の検査をしない + if (memcmp(hash_ctx.hash, files[num].hash + 16, 16) != 0){ + comp_num = -1; + goto error_end; + } + + // 16k MD5 が一致したら残りの部分も計算する + Phmd5Begin(&hash_ctx); // 初期化 + Phmd5Process(&hash_ctx, buf, 16384); // MD5 計算 + if (s_blk != NULL){ + for (off = 0; off < 16384; off += block_size){ + Phmd5Begin(&block_ctx); + if (off + block_size <= 16384){ + crc = crc_update(0, buf + off, block_size); + Phmd5Process(&block_ctx, buf + off, block_size); + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + find_next++; + } else { // スライスが途中までなら + crc = crc_update(0, buf + off, 16384 - off); + Phmd5Process(&block_ctx, buf + off, 16384 - off); + block_left = block_size - 16384 + off; + } + } + } + + // 次の分を読み込む + ol.Offset = 16384; + file_off = 16384 + io_size; + read_size = io_size; + if (file_left < io_size) + read_size = (unsigned int)file_left; + off = ReadFile(hFile, buf1, read_size, NULL, &ol); + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + goto error_end; // 読み取りが失敗した所で終わる + } + + while (file_left > 0){ + len = read_size; // 読み込んだサイズ + file_left -= read_size; + + // 前回の読み込みが終わるのを待つ + WaitForSingleObject(ol.hEvent, INFINITE); + + // 次の分を読み込み開始しておく + if (file_left > 0){ + read_size = io_size; + if (file_left < io_size) + read_size = (unsigned int)file_left; + ol.Offset = (unsigned int)file_off; + ol.OffsetHigh = (unsigned int)(file_off >> 32); + file_off += io_size; + off = ReadFile(hFile, buf, read_size, NULL, &ol); + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + goto error_end; // 読み取りが失敗した所で終わる + } + } + // バッファーを入れ替える + if (buf == buf1){ + buf = buf1 + io_size; + } else { + buf = buf1; + } + + if (s_blk != NULL){ + off = 0; + if (block_left > 0){ // 前回足りなかった分を追加する + if (block_left <= len){ + crc = crc_update(crc, buf, block_left); + Phmd5Process2(&hash_ctx, &block_ctx, buf, block_left); + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + find_next++; + off = block_left; + block_left = 0; + } else { + crc = crc_update(crc, buf, len); + Phmd5Process2(&hash_ctx, &block_ctx, buf, len); + off = len; + block_left -= len; + } + } + for (; off < len; off += block_size){ + Phmd5Begin(&block_ctx); + if (off + block_size <= len){ + // s_blk[].crc は初期値と最終処理の 0xFFFFFFFF を取り除いてる + crc = crc_update(0, buf + off, block_size); + Phmd5Process2(&hash_ctx, &block_ctx, buf + off, block_size); + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + find_next++; + } else { // スライスが途中までなら + crc = crc_update(0, buf + off, len - off); + Phmd5Process2(&hash_ctx, &block_ctx, buf + off, len - off); + block_left = block_size - len + off; + } + } + } else { + Phmd5Process(&hash_ctx, buf, len); // MD5 計算 + } + + // 経過更新 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (file_th->flag < 0) // キャンセル指示が出た + goto error_end; + // 上位 12-bit にパーセント、下位 20-bit に検出ブロック数を記録する + file_th->rv = ((int)(((file_size - file_left) * 1000) / file_size) << 20) | comp_num; + time_last = GetTickCount(); + } + } + Phmd5End(&hash_ctx); // 最終処理 + + if ((s_blk != NULL) && (block_left > 0)){ // 末尾の半端なスライスがあるなら + crc = crc_update_zero(crc, block_left); + Phmd5ProcessZero(&block_ctx, block_left); + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + } + + if (memcmp(hash_ctx.hash, files[num].hash, 16) == 0) // ファイル全域の MD5 が同じ + comp_num = -3; + +error_end: + CancelIo(hFile); // 非同期 IO を取り消す + CloseHandle(hFile); // ファイルはサブ・スレッド側で閉じる + if (ol.hEvent) + CloseHandle(ol.hEvent); + if (buf1) + _aligned_free(buf1); + file_th->rv = comp_num; + + return 0; +} + +// キャッシュ無しでファイルのハッシュ値が同じか調べる +// 0~=破損してるが何個目まで同じ, -1=MD5-16kが異なる, -2=キャンセル, -3=最後まで同じ(完全), -4=存在しない, -5=追加 +int file_hash_direct( + int num, // file_ctx におけるファイル番号 + wchar_t *file_path, // ハッシュ値を求めるファイル + wchar_t *file_name, // 表示するファイル名 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk) // 各ソース・ブロックの情報 (NULL なら比較しない) +{ + unsigned char *buf, *buf1 = NULL; + int find_next, comp_num = 0; + unsigned int len, off, crc, block_left = 0, read_size; + unsigned int time_last; + __int64 file_size, file_left, file_off; + PHMD5 hash_ctx, block_ctx; + HANDLE hFile; + OVERLAPPED ol; +#ifdef TIMER +time1_start = GetTickCount(); +#endif + + prog_last = -1; // 検証中のファイル名を毎回表示する + time_last = GetTickCount(); + file_size = files[num].size; // 本来のファイル・サイズ + find_next = files[num].b_off; // 先頭ブロックの番号 + + // ソース・ファイルを開く + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + off = GetLastError(); + if (off == 2) // file not found (2) + return -4; + if ((off == 32) || // ERROR_SHARING_VIOLATION + (off == 33)){ // ERROR_LOCK_VIOLATION + Sleep(300); // 少し待ってから再挑戦する + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); + } else { + //printf("cannot set FILE_FLAG_NO_BUFFERING %d\n", GetLastError()); + // ディスク・キャッシュを無効にできない場合 + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + } + if (hFile == INVALID_HANDLE_VALUE) + return -4; + } + + // 非同期ファイル・アクセスの準備をする + memset(&ol, 0, sizeof(OVERLAPPED)); + ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (ol.hEvent == NULL){ + comp_num = -1; + goto error_end; + } + + // バッファーの境界をセクター・サイズの倍数にする + // IO_SIZE は 16KB 以上で 4KB の倍数にすること + buf1 = _aligned_malloc(IO_SIZE * 2, 4096); + if (buf1 == NULL){ + printf("malloc, %d\n", IO_SIZE * 2); + comp_num = -1; + goto error_end; + } + buf = buf1 + IO_SIZE; + + // まずは先頭 16KB のハッシュ値を比較する + if (file_size < 16384){ + len = (unsigned int)file_size; + read_size = (len + 4095) & ~4095; // 4KB の倍数にする + file_left = 0; + } else { + len = 16384; + read_size = 16384; + file_left = file_size - 16384; // 本来のファイル・サイズまでしか検査しない + } +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = ReadFile(hFile, buf, read_size, NULL, &ol); +#ifdef TIMER +time2_total += GetTickCount() - time_start; +#endif + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + comp_num = -1; + goto error_end; + } + WaitForSingleObject(ol.hEvent, INFINITE); + + Phmd5Begin(&hash_ctx); // 初期化 + Phmd5Process(&hash_ctx, buf, len); // MD5 計算 + Phmd5End(&hash_ctx); // 最終処理 + if (file_left == 0){ // ファイルは 16KB 以下 + if (s_blk != NULL){ // 全てのスライスを比較する + for (off = 0; off < len; off += block_size){ + Phmd5Begin(&block_ctx); + if (off + block_size <= len){ + crc = crc_update(0, buf + off, block_size); + Phmd5Process(&block_ctx, buf + off, block_size); + } else { // 末尾の半端なスライスなら + crc = crc_update(0, buf + off, len - off); + Phmd5Process(&block_ctx, buf + off, len - off); + block_left = block_size - len + off; // ブロック・サイズまで 0で埋めて計算する + crc = crc_update_zero(crc, block_left); + Phmd5ProcessZero(&block_ctx, block_left); + } + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + find_next++; + } + } + if (memcmp(hash_ctx.hash, files[num].hash + 16, 16) != 0){ // 16k MD5 が異なる + comp_num = -1; + goto error_end; + } + comp_num = -3; // 同じ + + // ファイルサイズが大きい場合は末尾に追加されてる + if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_left)) + file_left = 0; + if (file_left != file_size) + comp_num = -5; + + goto error_end; + } + // 16KB よりも大きくて 16k MD5 が異なる場合は、スライス単位の検査をしない + if (memcmp(hash_ctx.hash, files[num].hash + 16, 16) != 0){ + comp_num = -1; + goto error_end; + } + + // 16k MD5 が一致したら残りの部分も計算する + Phmd5Begin(&hash_ctx); // 初期化 + Phmd5Process(&hash_ctx, buf, 16384); // MD5 計算 + if (s_blk != NULL){ + for (off = 0; off < 16384; off += block_size){ + Phmd5Begin(&block_ctx); + if (off + block_size <= 16384){ + crc = crc_update(0, buf + off, block_size); + Phmd5Process(&block_ctx, buf + off, block_size); + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + find_next++; + } else { // スライスが途中までなら + crc = crc_update(0, buf + off, 16384 - off); + Phmd5Process(&block_ctx, buf + off, 16384 - off); + block_left = block_size - 16384 + off; + } + } + } + + // 次の分を読み込む + ol.Offset = 16384; + file_off = 16384 + IO_SIZE; + read_size = IO_SIZE; + if (file_left < IO_SIZE){ + read_size = (unsigned int)file_left; + read_size = (read_size + 4095) & ~4095; // 4KB の倍数にする + } +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = ReadFile(hFile, buf1, read_size, NULL, &ol); +#ifdef TIMER +time2_total += GetTickCount() - time_start; +#endif + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + goto error_end; // 読み取りが失敗した所で終わる + } + + while (file_left > 0){ + len = IO_SIZE; // 読み込んだサイズ + if (file_left < IO_SIZE) + len = (unsigned int)file_left; + file_left -= len; + + // 前回の読み込みが終わるのを待つ + WaitForSingleObject(ol.hEvent, INFINITE); + + // 次の分を読み込み開始しておく + if (file_left > 0){ + read_size = IO_SIZE; + if (file_left < IO_SIZE){ + read_size = (unsigned int)file_left; + read_size = (read_size + 4095) & ~4095; // 4KB の倍数にする + } + ol.Offset = (unsigned int)file_off; + ol.OffsetHigh = (unsigned int)(file_off >> 32); + file_off += IO_SIZE; +#ifdef TIMER +time_start = GetTickCount(); +#endif + off = ReadFile(hFile, buf, read_size, NULL, &ol); +#ifdef TIMER +time2_total += GetTickCount() - time_start; +#endif + if ((off == 0) && (GetLastError() != ERROR_IO_PENDING)){ + print_win32_err(); + goto error_end; // 読み取りが失敗した所で終わる + } + } + // バッファーを入れ替える + if (buf == buf1){ + buf = buf1 + IO_SIZE; + } else { + buf = buf1; + } + +#ifdef TIMER +time_start = GetTickCount(); +#endif + if (s_blk != NULL){ + off = 0; + if (block_left > 0){ // 前回足りなかった分を追加する + if (block_left <= len){ + crc = crc_update(crc, buf, block_left); + Phmd5Process2(&hash_ctx, &block_ctx, buf, block_left); + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + find_next++; + off = block_left; + block_left = 0; + } else { + crc = crc_update(crc, buf, len); + Phmd5Process2(&hash_ctx, &block_ctx, buf, len); + off = len; + block_left -= len; + } + } + for (; off < len; off += block_size){ + Phmd5Begin(&block_ctx); + if (off + block_size <= len){ + crc = crc_update(0, buf + off, block_size); + Phmd5Process2(&hash_ctx, &block_ctx, buf + off, block_size); + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + find_next++; + } else { // スライスが途中までなら + crc = crc_update(0, buf + off, len - off); + Phmd5Process2(&hash_ctx, &block_ctx, buf + off, len - off); + block_left = block_size - len + off; + } + } + } else { + Phmd5Process(&hash_ctx, buf, len); // MD5 計算 + } +#ifdef TIMER +time3_total += GetTickCount() - time_start; +#endif + + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress_file((int)(((file_size - file_left) * 1000) / file_size), -1, file_name)){ + comp_num = -2; + goto error_end; + } + time_last = GetTickCount(); + } + } + + if ((s_blk != NULL) && (block_left > 0)){ // 末尾の半端なスライスがあるなら + crc = crc_update_zero(crc, block_left); + Phmd5ProcessZero(&block_ctx, block_left); + Phmd5End(&block_ctx); + if ((crc != s_blk[find_next].crc) || (memcmp(block_ctx.hash, s_blk[find_next].hash, 16) != 0)) + goto error_end; // スライスの CRC-32 か MD5 が異なる + comp_num++; + } + + Phmd5End(&hash_ctx); // 最終処理 + if (memcmp(hash_ctx.hash, files[num].hash, 16) == 0) // ファイル全域の MD5 が同じ + comp_num = -3; + + // ファイルサイズが大きい場合は末尾に追加されてる + if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_left)) + file_left = 0; + if (file_left != file_size) + comp_num = -5; + +error_end: + CancelIo(hFile); // 非同期 IO を取り消す + CloseHandle(hFile); + if (ol.hEvent) + CloseHandle(ol.hEvent); + if (buf1) + _aligned_free(buf1); + +#ifdef TIMER +time_total += GetTickCount() - time1_start; + printf("\nread %d.%03d sec\n", time2_total / 1000, time2_total % 1000); + printf("main %d.%03d sec\n", time3_total / 1000, time3_total % 1000); + printf("total %d.%03d sec\n", time_total / 1000, time_total % 1000); +#endif + return comp_num; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// CRC-32 を使って 1バイト内のバースト・エラーを訂正する +// -1=エラー訂正できず, 0=エラー無し, 1=エラー訂正できた +// 全て 0 のデータの判定は他で行うこと +int correct_error( + unsigned char *data_in, // ハッシュ値を求めたバイト配列 + unsigned int data_len, // 入力バイト数 + unsigned char *hash, // ブロック・サイズ分の本来のハッシュ値 (16バイト, MD5) + unsigned int crc, // 入力バイト数分の本来の CRC-32 + unsigned int *error_off, // エラー開始位置 + unsigned char *error_mag) // エラー内容、XORでエラー訂正を取り消せる +{ + unsigned char data_hash[16]; + unsigned int data_crc, pos; + + // まずエラーが存在するかを調べる + data_crc = crc_update(0, data_in, data_len); + if (data_crc == crc){ + // MD5 はブロック・サイズ分を計算する + data_md5_block(data_in, data_len, data_hash); + if (memcmp(data_hash, hash, 16) == 0) + return 0; // エラー無し + return -1; // CRC-32 でエラーを検出できない場合は訂正もできない + } + if (data_len > 0x100000) + return -1; // 1MB 以上ならエラー訂正しない + // CRC-32 で 1バイト訂正を試みると、失敗する確率はエラー位置ごとに 1 / 2^24 ぐらい? + // データ・サイズが 1MB (1^20) だと、訂正できなくても 1/16 の確率で MD5 による検算が必要 + + // エラー位置と程度を計算する + crc ^= data_crc; // CRC-32 の差にする + for (pos = 1; pos <= data_len; pos++){ + // 1 バイトずつ遡りながら最初のエラー発生位置を探す + crc = reverse_table[(crc >> 24)] ^ (crc << 8); + + // 一定範囲内のエラーを訂正する + if ((crc & 0xFFFFFF00) == 0){ + // 試しにエラーを訂正してみる + *error_mag = (unsigned char)crc; + data_in[data_len - pos] ^= *error_mag; // エラーが発生した 1バイト目 + + // 正しく訂正できてるかを確認する + data_md5_block(data_in, data_len, data_hash); + if (memcmp(data_hash, hash, 16) == 0){ + //printf("position = %d byte from end, magnitude = 0x%08X\n", pos - 1, crc); + *error_off = data_len - pos; + return 1; // エラー訂正できた + } + // 訂正失敗ならデータを元に戻しておく + //printf("MD5 found errors\n"); + data_in[data_len - pos] ^= *error_mag; + } + } + + return -1; // 訂正できず +} + +// CRC-32 を使って 1バイト内のバースト・エラーを訂正する +// -1=エラー訂正できず, 0=エラー無し, 1=エラー訂正できた +// CRC-32 は既に計算済み、エラー無しも比較済み +int correct_error2( + unsigned char *data_in, // ハッシュ値を求めたバイト配列 + unsigned int data_len, // 入力バイト数 + unsigned int data_crc, // 入力バイト数分の CRC-32 + unsigned char *hash, // ブロック・サイズ分の本来のハッシュ値 (16バイト, MD5) + unsigned int crc, // 入力バイト数分の本来の CRC-32 + unsigned int *error_off, // エラー開始位置 + unsigned char *error_mag) // エラー内容、XORでエラー訂正を取り消せる +{ + unsigned char data_hash[16]; + unsigned int pos; + + if (data_len > 0x100000) + return -1; // 1MB 以上ならエラー訂正しない + + // エラー位置と程度を計算する + crc ^= data_crc; // CRC-32 の差にする + for (pos = 1; pos <= data_len; pos++){ + // 1 バイトずつ遡りながら最初のエラー発生位置を探す + crc = reverse_table[(crc >> 24)] ^ (crc << 8); + + // 一定範囲内のエラーを訂正する + if ((crc & 0xFFFFFF00) == 0){ + // 試しにエラーを訂正してみる + *error_mag = (unsigned char)crc; + data_in[data_len - pos] ^= *error_mag; // エラーが発生した 1バイト目 + + // 正しく訂正できてるかを確認する + data_md5_block(data_in, data_len, data_hash); + if (memcmp(data_hash, hash, 16) == 0){ + //printf("position = %d byte from end, magnitude = 0x%08X\n", pos - 1, crc); + *error_off = data_len - pos; + return 1; // エラー訂正できた + } + // 訂正失敗ならデータを元に戻しておく + //printf("MD5 found errors\n"); + data_in[data_len - pos] ^= *error_mag; + } + } + + return -1; // 訂正できず +} + +/* +// MD5 と CRC32 から 8バイトのデータを逆算するための関数 +void data_reverse8( + unsigned char *data_out, // 不明な入力データ (8バイト) + unsigned char *hash, // ハッシュ値 (16バイト) + unsigned char *crc) // CRC (4バイト) +{ + unsigned int i, rv, crc1, crc2, data[16], state[4], time; + // crc = 0xffffffff ^ data[0~3]; // 初期値と最初のデータを XOR する + // crc = table[crc & 0xff] ^ (crc >> 8); + // crc = table[crc & 0xff] ^ (crc >> 8); + // crc = table[crc & 0xff] ^ (crc >> 8); + // crc = table[crc & 0xff] ^ (crc >> 8); + // crc ^= data[4~7]; // 結果と次のデータを XOR する + // crc = table[crc & 0xff] ^ (crc >> 8); + // crc = table[crc & 0xff] ^ (crc >> 8); + // crc = table[crc & 0xff] ^ (crc >> 8); + // crc = table[crc & 0xff] ^ (crc >> 8); + // crc ^= 0xffffffff; 最終処理 + +time = GetTickCount(); + + memcpy(&crc2, crc, 4); + crc2 ^= 0xffffffff; // 最終処理を取り消す + rv = CrcReverse4(crc2); // 2回目のCRC計算を取り消した値 = crc ^ data[4~7] + // 1回目のCRC計算の結果がわからないので、入力データもわからない (XORした値だけわかってる) + // そこで、入力データを変化させながら1回目のCRCを逆算して、候補をMD5で確認する + for (i = 0; i < 16; i++) + data[i] = 0; + data[2] = 0x00000080; // データ終端の 1bit + data[14] = 0x00000040; // データ・サイズ 8byte = 64bit + do { + crc1 = rv ^ data[1]; + data[0] = CrcReverse4(crc1) ^ 0xffffffff; // 逆算して初期化処理を取り消す + if ((data[1] & 0x00ffffff) == 0) + printf("%08x%08x\n", data[0], data[1]); + // このデータで合ってるかをMD5で調べる + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + MD5Transform(data, state); + + if (memcmp(hash, state, 16) == 0) + break; + data[1]++; + } while (data[1] != 0); + + memcpy(data_out, (unsigned char *)data, 8); +// Pentium 3 800MHz だと完全走査に約50分かかる、テキストなら20分ぐらい? +time = (GetTickCount() - time); +printf("\n%3u.%03u sec\n", time / 1000, time % 1000); +} +*/ + diff --git a/source/par2j/md5_crc.h b/source/par2j/md5_crc.h new file mode 100644 index 0000000..6460ad9 --- /dev/null +++ b/source/par2j/md5_crc.h @@ -0,0 +1,128 @@ +#ifndef _MD5_H +#define _MD5_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// バイト配列の MD5 ハッシュ値を求める +void data_md5( + unsigned char *data_in, // ハッシュ値を求めるバイト配列 + unsigned int data_len, // 入力バイト数 + unsigned char *hash); // ハッシュ値 16バイト + +// ブロックの MD5 ハッシュ値を求める +void data_md5_block( + unsigned char *data_in, // ハッシュ値を求めるバイト配列 + unsigned int data_len, // 入力バイト数 + unsigned char *hash); // ハッシュ値 16バイト + +// ファイルの MD5-16k ハッシュ値を求める +int file_md5_16( + wchar_t *file_path, // ハッシュ値を求めるファイル + unsigned char *hash); // ファイルの先頭 16KB 分のハッシュ値 (16バイト) + +// ファイルの指定部分の MD5 ハッシュ値を求める +int file_md5( + HANDLE hFileRead, // ハッシュ値を求めるファイルのハンドル + __int64 offset, + unsigned int data_len, // 入力バイト数 + unsigned char *hash); // ハッシュ値 (16バイト) + +/*------------------ 以下は MD5 と CRC-32 の組合せ関数 ------------------*/ + +// 内容が全て 0 のデータの MD5 ハッシュ値と CRC-32 を求める +void data_md5_crc32_zero( + unsigned char *hash); // ハッシュ値 (16 + 4バイト, MD5 + CRC-32) + +// 2GB 未満のファイルの開始位置以降のハッシュ値を計算する +unsigned int file_crc_part(HANDLE hFile); + +// ファイルの offset バイト目からブロック・サイズ分の MD5 と CRC-32 を求める +int file_md5_crc32_block( + HANDLE hFileRead, // MD5 と CRC を求めるファイルのハンドル + __int64 offset, + unsigned int avail_size, // 入力バイト数 + unsigned char *hash); // ハッシュ値 (16 + 4バイト, MD5 + CRC-32) + +// ファイルのハッシュ値と各スライスのチェックサムを同時に計算する +int file_hash_crc( + wchar_t *file_name, // ハッシュ値を求めるファイル + __int64 file_left, + unsigned char *hash, // ハッシュ値 (16バイト) + unsigned char *sum, // チェックサム (MD5 + CRC-32) の配列 + unsigned int *time_last, // 前回に経過表示した時刻 + __int64 *prog_now); // 経過表示での現在位置 + +typedef struct { + wchar_t *file_name; + __int64 file_size; + unsigned char *hash; + unsigned char *sum; + unsigned int *crc; + volatile int loop; +} FILE_HASH_TH; + +DWORD WINAPI file_hash_crc2(LPVOID lpParameter); + +// ファイルのハッシュ値が同じか調べる (全てのスライスのチェックサムも) +int file_hash_check( + int num, // file_ctx におけるファイル番号 + wchar_t *file_name, // 表示するファイル名 + HANDLE hFile, // ファイルのハンドル + int prog_min, // 検出数がこの値を超えたら合計数を表示する (極大なら表示しない) + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 (NULL なら比較しない) + +typedef struct { + HANDLE hFile; + file_ctx_r *files; + source_ctx_r *s_blk; + int num; + volatile int rv; + volatile int flag; + unsigned int meta[7]; +} FILE_CHECK_TH; + +DWORD WINAPI file_hash_background(LPVOID lpParameter); + +// キャッシュ無しでファイルのハッシュ値が同じか調べる +int file_hash_direct( + int num, // file_ctx におけるファイル番号 + wchar_t *file_path, // ハッシュ値を求めるファイル + wchar_t *file_name, // 表示するファイル名 + file_ctx_r *files, // 各ソース・ファイルの情報 + source_ctx_r *s_blk); // 各ソース・ブロックの情報 (NULL なら比較しない) + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// CRC-32 を使って 1バイト内のバースト・エラーを訂正する +// 全て 0 のデータの判定は他で行うこと +int correct_error( + unsigned char *data_in, // ハッシュ値を求めたバイト配列 + unsigned int data_len, // 入力バイト数 + unsigned char *hash, // ブロック・サイズ分のハッシュ値 (16バイト, MD5) + unsigned int orig_crc, // 入力バイト数分の CRC-32 + unsigned int *error_off, // エラー開始位置 + unsigned char *error_mag); // エラー内容、XORでエラー訂正を取り消せる + +// CRC-32 を使って 1バイト内のバースト・エラーを訂正する +// CRC-32 は既に計算済み、エラー無しも比較済み +int correct_error2( + unsigned char *data_in, // ハッシュ値を求めたバイト配列 + unsigned int data_len, // 入力バイト数 + unsigned int data_crc, // 入力バイト数分の CRC-32 + unsigned char *hash, // ブロック・サイズ分のハッシュ値 (16バイト, MD5) + unsigned int crc, // 入力バイト数分の CRC-32 + unsigned int *error_off, // エラー開始位置 + unsigned char *error_mag); // エラー内容、XORでエラー訂正を取り消せる + + +#ifdef __cplusplus +} +#endif + +#endif