From e218f5f634d9840d433ccabdab566cbe7c5011e2 Mon Sep 17 00:00:00 2001 From: Yutaka Sawada <60930312+Yutaka-Sawada@users.noreply.github.com> Date: Sun, 12 Mar 2023 10:41:47 +0900 Subject: [PATCH] Add files via upload --- source/par1j/common1.c | 1048 +++++++++++++ source/par1j/common1.h | 134 ++ source/par1j/gf8.c | 205 +++ source/par1j/gf8.h | 67 + source/par1j/ini.c | 446 ++++++ source/par1j/ini.h | 32 + source/par1j/md5_1.c | 223 +++ source/par1j/md5_1.h | 48 + source/par1j/par1.c | 3031 +++++++++++++++++++++++++++++++++++++ source/par1j/par1.h | 46 + source/par1j/par1_cmd.c | 753 +++++++++ source/par1j/phmd5.c | 79 + source/par1j/phmd5.h | 59 + source/par1j/phmd5a.c | 130 ++ source/par1j/res_par1j.rc | Bin 0 -> 862 bytes source/par1j/version.h | 2 + 16 files changed, 6303 insertions(+) create mode 100644 source/par1j/common1.c create mode 100644 source/par1j/common1.h create mode 100644 source/par1j/gf8.c create mode 100644 source/par1j/gf8.h create mode 100644 source/par1j/ini.c create mode 100644 source/par1j/ini.h create mode 100644 source/par1j/md5_1.c create mode 100644 source/par1j/md5_1.h create mode 100644 source/par1j/par1.c create mode 100644 source/par1j/par1.h create mode 100644 source/par1j/par1_cmd.c create mode 100644 source/par1j/phmd5.c create mode 100644 source/par1j/phmd5.h create mode 100644 source/par1j/phmd5a.c create mode 100644 source/par1j/res_par1j.rc create mode 100644 source/par1j/version.h diff --git a/source/par1j/common1.c b/source/par1j/common1.c new file mode 100644 index 0000000..aad5010 --- /dev/null +++ b/source/par1j/common1.c @@ -0,0 +1,1048 @@ +// common.c +// Copyright : 2022-02-05 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 "common1.h" + + +// グローバル変数 +wchar_t recovery_file[MAX_LEN]; // リカバリ・ファイルのパス +wchar_t base_dir[MAX_LEN]; // ソース・ファイルの基準ディレクトリ +wchar_t ini_path[MAX_LEN]; // 検査結果ファイルのパス + +unsigned int cp_output; + +/* +// Console Input Code Page から Windown OS の precomposed UTF-16 に変換する +int cp_to_utf16(char *in, wchar_t *out) +{ + if (!MultiByteToWideChar(cp_input, 0, in, -1, out, MAX_LEN)){ + wcscpy(out, L"cannot encode"); + return 1; + } + return 0; +} +*/ + +// Windown OS の precomposed UTF-16 から Console Output Code Page に変換する +int utf16_to_cp(wchar_t *in, char *out) +{ + if (cp_output == CP_UTF8){ + if (WideCharToMultiByte(CP_UTF8, 0, in, -1, out, MAX_LEN * 3, NULL, NULL) == 0){ + strcpy(out, "cannot encode"); + return 1; + } + return 0; + } + + // 似た文字への自動変換を行わない + if (!WideCharToMultiByte(cp_output, WC_NO_BEST_FIT_CHARS, in, -1, out, MAX_LEN * 2, NULL, NULL)){ + strcpy(out, "cannot encode"); + return 1; + } + return 0; +} + +// ファイル・パスから、先頭にある "\\?\" を省いて、Console Output Code Page に変換する +int path_to_cp(wchar_t *path, char *out) +{ + unsigned int dwFlags = WC_NO_BEST_FIT_CHARS; // 似た文字への自動変換を行わない + + if (cp_output == 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_output, dwFlags, path, -1, out, MAX_LEN * 3, NULL, NULL) == 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); + printf(format, buf); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ファイル・パスがファイル・リスト上に既に存在するか調べる +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) // 大文字と小文字を区別する + 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 += (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); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ファイル・パスからファイル名の位置を戻す +wchar_t * offset_file_name(wchar_t *file_path) +{ + int i; + + for (i = 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 = 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 = 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; +} + +// 絶対パスかどうかを判定する +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; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#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); + + // 相対パスに対して基準となるディレクトリが指定されてるなら + if ((dir_path != NULL) && (is_full_path(src_path) == 0)){ + // そのまま基準ディレクトリを連結して絶対パスにする + len = 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); + 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 = 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ファイルや「*?」で検索するソース・ファイル) + int name_len; + // 区切り文字を探す + name_len = wcslen(new_path); + for (len = name_len - 1; len >= 0; len--){ + if (new_path[len] == '\\') + break; + } + len++; + wcscpy(tmp_path, new_path + len); // 存在しないファイル名を記録しておく + name_len -= 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(); + return 0; + } else { // 退避させておいたファイル名を追加する + len += name_len; + 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 wcslen(file_path); // そのままの長さ + + // "\\?\UNC\\" ならネットワーク・パス + if (wcsncmp(file_path + PREFIX_LEN, L"UNC\\", 4) == 0) + return wcslen(file_path + PREFIX_LEN + 2); // "\\?\UNC\" -> "\\" なので6文字減らす + + // "\\?\\" なら + return wcslen(file_path + PREFIX_LEN); // "\\?\" を省くので4文字減らす +} + +// ファイル・パスから、先頭にある "\\?\" を省いてコピーする +int copy_without_prefix( + wchar_t *dst_path, // コピー先 (コピー元と同じアドレスでもよい) + wchar_t *src_path) // コピー元のパス +{ + int i, len; + + len = 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 replace_file( + wchar_t *dest_path, // 置き換える先のパス (移動先、修正されたファイル名) + wchar_t *sorc_path, // 置き換える元のパス (移動元、現在のファイル名) + int switch_b) // 既存のファイルをバックアップするかどうか +{ + unsigned int rv, num; + + // 移動元のファイルが存在するかどうか + num = GetFileAttributes(sorc_path); + if (num == INVALID_FILE_ATTRIBUTES){ + return 1; // 移動させるファイルが存在しない + } else if (num & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性なら、その属性を解除する + if (SetFileAttributes(sorc_path, num ^ FILE_ATTRIBUTE_READONLY) == 0) + return 2; // 移動元の読み取り専用属性を解除できなかった + } + + // 移動先のファイルが存在するかどうか + rv = GetFileAttributes(dest_path); + if (rv == INVALID_FILE_ATTRIBUTES){ // ファイルが存在しないなら、そのまま移動する + if (MoveFile(sorc_path, dest_path) == 0) + return 4; // 移動失敗 + 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){ // 既存のファイルを削除せずに別名で残す + 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){ // バックアップを作って置き換える + num = ReplaceFile(dest_path, sorc_path, back_path, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0); + if (num == 0){ + num = GetLastError(); + if ((num == 32) || // The process cannot access the file because it is being used by another process. + (num == 1175) || // ERROR_UNABLE_TO_REMOVE_REPLACED + (num == 1176)){ // ERROR_UNABLE_TO_MOVE_REPLACEMENT + Sleep(100); + 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(100); + num = MoveFile(sorc_path, dest_path); // 既存ファイルはバックアップ済みなら、移動するだけ + } else { + num = 0; + } + if (num == 0) + return 6; + } + 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); + if (num == 0){ + num = GetLastError(); + if ((num == 32) || // The process cannot access the file because it is being used by another process. + (num == 1175)){ // ERROR_UNABLE_TO_REMOVE_REPLACED + Sleep(100); + num = ReplaceFile(dest_path, sorc_path, NULL, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0); + } else if (num == 1176){ // ERROR_UNABLE_TO_MOVE_REPLACEMENT + Sleep(100); + num = MoveFile(sorc_path, dest_path); // 既存ファイルは削除済みなら、移動するだけ + } else { + num = 0; + } + if (num == 0) + return 8; + } + if (rv & FILE_ATTRIBUTE_READONLY){ // 読み取り専用属性を付け直す + rv = GetFileAttributes(dest_path); + SetFileAttributes(dest_path, rv | FILE_ATTRIBUTE_READONLY); // 失敗してもエラーにしない + } + } + } + + return 0; +} + +// ファイルを指定サイズに縮小する +int shorten_file( + wchar_t *file_path, // ファイル・パス + __int64 new_size, + int switch_b) // 既存のファイルをバックアップするかどうか +{ + 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){ // 既存のファイルを削除せずに別名で残す + 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] == '.')){ + 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] == '.')){ + 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=警告, 16=エラー +int sanitize_filename(wchar_t *name) +{ + int i, 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) + return 16; + + // WinOS ではファイル名に「\/:*?"<>|」の文字は使えないので置換する + for (i = 0; i < len; i++){ + if ((name[i] == '\\') || (name[i] == '/') || (name[i] == ':') + || (name[i] == '*') || (name[i] == '?') || (name[i] == '"') + || (name[i] == '<') || (name[i] == '>') || (name[i] == '|')){ + name[i] = '_'; // 「\,/,:,*,?,",<,>,|」 ->「_」 + rv |= 2; + } + } + + // 先頭の「 」、末尾の「.」「 」を警告する + if ((name[0] == ' ') || (name[len - 1] == '.') || (name[len - 1] == ' ')) + rv |= 8; + + // デバイス名を警告する + if (check_device_name(name, len) != 0) + rv |= 8; + + if (len == 0) // 最終的にファイル名が無くなればエラー + return 16; + return rv; +} + +// 修復中のテンポラリ・ファイルの名前を作る +// 末尾に _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"); // 末尾に追加する +} + +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]); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +unsigned int memory_use; // メモリー使用量 0=auto, 1~7 -> 1/8 ~ 7/8 + +// 空きメモリー量と制限値から使用できるメモリー量を計算する +unsigned int get_mem_size(unsigned __int64 data_size) +{ + unsigned int mem_size, *tmp_p; + 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 (data_size + cache_size < statex.ullAvailPhys){ // all file data can be cached in memory + mem_size64 = statex.ullAvailPhys - data_size; // using size = available size - data size + if (memory_use){ // limited upto ?/8 + cache_size *= memory_use; + if (mem_size64 > cache_size) + mem_size64 = cache_size; + } + } else { + if (memory_use){ // manual setting + mem_size64 = cache_size * memory_use; // using size = available size / 8 * memory_use + } else { // auto setting (mostly for 32-bit OS) + // disk cache size is range from 100MB to 200MB. + cache_size += statex.ullTotalPhys >> 4; // cache size = available size / 8 + total size / 16 + 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; // keep 2MB for other task + if (mem_size64 >= 0x7F000000){ // max is 2032MB + mem_size = 0x7F000000; + } else { + mem_size = (unsigned int)mem_size64 & 0xFFFF0000; // multiple of 64KB + } + + } else { + print_win32_err(); + mem_size = 0x20000000; // メモリー量を認識できない場合は 512MB と仮定する + } + + // try to allocate for test + //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 -= 0x01000000; // reduce 16MB + tmp_p = malloc(mem_size); + } + if (tmp_p != NULL) + free(tmp_p); + //printf("%d MB ok\n", mem_size >> 20); + + return mem_size; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define MAX_NAME_LEN 69 // 経過表示のタイトルの最大文字数 (末尾の null 文字を含む) + +int prog_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, wchar_t *file_name) +{ + 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) && (file_name != NULL)){ // 初めて経過を表示する時だけファイル名を表示する + 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); + 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; +} + +// 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_cp(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); +} + +// ファイルをゴミ箱に移す +int delete_file_recycle(wchar_t *file_path) +{ + 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); + } 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; // 削除がキャンセルされた + return rv; +} + diff --git a/source/par1j/common1.h b/source/par1j/common1.h new file mode 100644 index 0000000..3a70ab1 --- /dev/null +++ b/source/par1j/common1.h @@ -0,0 +1,134 @@ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +#define MAX_LEN 1024 // ファイル名の最大文字数 (末尾のNULL文字も含む) +#define ADD_LEN 8 // 作業中にファイル名に追加する文字数 +#define EXT_LEN 16 // 拡張子として認識する最大文字数 +#define COMMENT_LEN 128 // コメントの最大文字数 +#define ALLOC_LEN 4096 // 可変長領域を何バイトごとに確保するか +#define IO_SIZE 65536 +#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 unsigned int cp_output; + +// Windown OS の precomposed UTF-16 から Console Output Code Page に変換する +int utf16_to_cp(wchar_t *in, char *out); + +// UTF-16 のファイル・パスを画面出力用の Code Page を使って表示する +void printf_cp(unsigned char *format, wchar_t *path); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ファイル・パスがファイル・リスト上に既に存在するか調べる +int search_file_path( + wchar_t *list, // ファイル・リスト + int total_len, // ファイル・リストの文字数 + wchar_t *search_file); // 検索するファイルのパス + +// ファイル・リストの内容を並び替える +void sort_list( + wchar_t *list, // ファイル・リスト + int total_len); // ファイル・リストの文字数 + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// ファイル・パスからファイル名の位置を戻す +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); // ディレクトリ + +// 絶対パスかどうかを判定する +int is_full_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 replace_file( + wchar_t *dest_path, // 置き換える先のパス (移動先、修正されたファイル名) + wchar_t *sorc_path, // 置き換える元のパス (移動元、現在のファイル名) + int switch_b); // 既存のファイルをバックアップするかどうか + +// ファイルを指定サイズに縮小する +int shorten_file( + wchar_t *file_path, // ファイル・パス + __int64 new_size, + int switch_b); // 既存のファイルをバックアップするかどうか + +// ファイル名が有効か確かめて、問題があれば浄化する +int sanitize_filename(wchar_t *name); + +// 修復中のテンポラリ・ファイルの名前を作る +void get_temp_name( + wchar_t *file_path, // ファイル・パス + wchar_t *temp_path); // テンポラリ・ファイルのパス + +void print_hash(unsigned char hash[16]); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +extern unsigned int memory_use; // メモリー使用量 0=auto, 1~7 -> 1/8 ~ 7/8 + +// 空きメモリー量と制限値から使用できるメモリー量を計算する +unsigned int get_mem_size(unsigned __int64 data_size); + +extern int prog_last; // 前回と同じ進捗状況は出力しないので記録しておく + +// 経過のパーセント表示、キャンセルと一時停止ができる +// 普段は 0 を返す、キャンセル時は 0以外 +int print_progress(int prog_now); // 表示する % 値 +void print_progress_text(int prog_now, char *text); +int print_progress_file(int prog_now, wchar_t *file_name); +void print_progress_done(void); + +// キャンセルと一時停止を行う +int cancel_progress(void); + +// Win32 API のエラー・メッセージを表示する +void print_win32_err(void); + +// ファイルをゴミ箱に移す +int delete_file_recycle(wchar_t *file_path); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par1j/gf8.c b/source/par1j/gf8.c new file mode 100644 index 0000000..6a5b19f --- /dev/null +++ b/source/par1j/gf8.c @@ -0,0 +1,205 @@ +// This is a sub-set of Galois.c +// Modified by Yutaka Sawada for 8-bit only + +/* Galois.c + * James S. Plank + * April, 2007 + +Galois.tar - Fast Galois Field Arithmetic Library in C/C++ +Copright (C) 2007 James S. Plank + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License aint with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +James S. Plank +Department of Computer Science +University of Tennessee +Knoxville, TN 37996 +plank@cs.utk.edu + +*/ + +#include + +#include "gf8.h" + +#define NW 256 +#define NWM1 255 +#define PRIM_POLY 0x11D + +static int *galois_log_table = NULL; +static int *galois_exp_table; + +static int *galois_mult_table = NULL; +static int *galois_div_table; + +int galois_create_log_tables(void) +{ + int j, b; + + if (galois_log_table != NULL) return 0; + galois_log_table = _aligned_malloc(sizeof(int) * NW * 4, 64); + if (galois_log_table == NULL) return -1; + galois_exp_table = galois_log_table + NW + 1; // 後で境界をあわせるため + + b = 1; + for (j = 0; j < NWM1; j++){ + galois_log_table[b] = j; + galois_exp_table[j] = b; + b = b << 1; + if (b & NW) b ^= PRIM_POLY; + } + galois_log_table[0] = -1; + + for (j = 0; j < NWM1; j++){ + galois_exp_table[j + NWM1 ] = galois_exp_table[j]; + galois_exp_table[j + NWM1 * 2] = galois_exp_table[j]; + } + galois_exp_table += NWM1; + return 0; +} + +int galois_create_mult_tables(void) +{ + int j, x, y, logx; + + if (galois_mult_table != NULL) return 0; + galois_mult_table = _aligned_malloc(sizeof(int) * NW * NW * 2, 64); + if (galois_mult_table == NULL) return -1; + galois_div_table = galois_mult_table + NW * NW; + + if (galois_log_table == NULL) { + if (galois_create_log_tables() < 0) { + _aligned_free(galois_mult_table); + galois_mult_table = NULL; + return -1; + } + } + + /* Set mult/div tables for x = 0 */ + j = 0; + galois_mult_table[j] = 0; /* y = 0 */ + galois_div_table[j] = -1; + j++; + for (y = 1; y < NW; y++) { /* y > 0 */ + galois_mult_table[j] = 0; + galois_div_table[j] = 0; + j++; + } + + for (x = 1; x < NW; x++){ /* x > 0 */ + galois_mult_table[j] = 0; /* y = 0 */ + galois_div_table[j] = -1; + j++; + logx = galois_log_table[x]; + for (y = 1; y < NW; y++){ /* y > 0 */ + galois_mult_table[j] = galois_exp_table[logx + galois_log_table[y]]; + galois_div_table[j] = galois_exp_table[logx - galois_log_table[y]]; + j++; + } + } + return 0; +} + +int galois_multtable_multiply(int x, int y) +{ + return galois_mult_table[(x << 8) | y]; +} + +int galois_multtable_divide(int x, int y) +{ + return galois_div_table[(x << 8) | y]; +} + +// ガロア体上での乗数計算、x の y 乗 +int galois_power(int x, int y) +{ + int sum_j; + + if (y == 0) return 1; // x^0 = 1 + sum_j = x; + while (y > 1){ + sum_j = galois_multtable_multiply(sum_j, x); + y--; + } + return sum_j; +} + +void galois_free_tables(void) // テーブルを一括して解放するために追加 +{ + if (galois_mult_table != NULL) { + _aligned_free(galois_mult_table); + galois_mult_table = NULL; + } + if (galois_log_table != NULL) { + _aligned_free(galois_log_table); + galois_log_table = NULL; + } +} + +void galois_region_xor( + unsigned char *r1, // Region 1 + unsigned char *r2, // Sum region (r2 = r1 ^ r2) + int nbytes) // Number of bytes in region +{ + int i; + + for (i = 0; i < nbytes; i++) + r2[i] ^= r1[i]; +} + +void galois_region_multiply( + unsigned char *r1, // Region to multiply + unsigned char *r2, // products go here. + int nbytes, // Number of bytes in region + int multby) // Number to multiply by +{ + int i, *table; + + table = galois_mult_table + (multby << 8); + for (i = 0; i < nbytes; i++) + r2[i] ^= table[ r1[i] ]; +} + +// チェックサムを計算する +void checksum4(unsigned char *data, unsigned char *hash, int byte_size) +{ + int i, count; + unsigned int *data4, temp, prev, mask; + + data4 = (unsigned int *)data; + count = byte_size / 4; + prev = 0; + + while (count > 0){ // HASH_RANGE バイトごとに + // 4バイトに XOR する + temp = 0; + for (i = 0; i < HASH_RANGE / 4; i++){ + temp ^= data4[i]; + count--; + if (count == 0) + break; + } + data4 += HASH_RANGE / 4; + + // 前回の値を 2倍して、今回の値を追加する + mask = (prev & 0x80808080) >> 7; // 0x01010101 or 0x00000000 + prev = (prev & 0x7F7F7F7F) << 1; // 3倍する場合は「^=」にすればいい + prev ^= mask * 0x1D; // 0x1D1D1D1D or 0x00000000 + + prev ^= temp; + } + *((unsigned int *)hash) = prev; +} + diff --git a/source/par1j/gf8.h b/source/par1j/gf8.h new file mode 100644 index 0000000..9050d44 --- /dev/null +++ b/source/par1j/gf8.h @@ -0,0 +1,67 @@ +// This is a sub-set of Galois.h , and changed for 8-bit only + +/* Galois.h + * James S. Plank + +Galois.tar - Fast Galois Field Arithmetic Library in C/C++ +Copright (C) 2007 James S. Plank + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +James S. Plank +Department of Computer Science +University of Tennessee +Knoxville, TN 37996 +plank@cs.utk.edu + + */ + +#ifndef _GF8_H_ +#define _GF8_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +extern int galois_create_log_tables(void); /* Returns 0 on success, -1 on failure */ +extern int galois_create_mult_tables(void); +extern int galois_multtable_multiply(int x, int y); +extern int galois_multtable_divide(int x, int y); + +extern int galois_power(int x, int y); // 乗数計算用に追加 +extern void galois_free_tables(void); // 解放用に追加 + +void galois_region_xor( + unsigned char *r1, // Region 1 + unsigned char *r2, // Sum region (r2 = r1 ^ r2) + int nbytes); // Number of bytes in region + +void galois_region_multiply( + unsigned char *r1, // Region to multiply + unsigned char *r2, // products go here. + int nbytes, // Number of bytes in region + int multby); // Number to multiply by + +#define HASH_SIZE 4 +#define HASH_RANGE 128 +void checksum4(unsigned char *data, unsigned char *hash, int byte_size); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par1j/ini.c b/source/par1j/ini.c new file mode 100644 index 0000000..4afa314 --- /dev/null +++ b/source/par1j/ini.c @@ -0,0 +1,446 @@ +// ini.c +// Copyright : 2022-01-15 Yutaka Sawada +// License : GPL + +#ifndef _UNICODE +#define _UNICODE +#endif +#ifndef UNICODE +#define UNICODE +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 // Windows Vista or later +#endif + +#include +#include + +#include + +#include "common1.h" +#include "ini.h" + +/* +ファイル名は 1_#.bin (2+32+4 = 38文字) +「#」部分は Set ID を 16進数表記にする。 + +ファイル・フォーマット + +2: 検査結果の書式バージョン +4: 6バイト目以降からの CRC-32 チェックサム + +4: ソース・ブロック数 (PAR1ではブロックを持つファイルだけが Set ID に反映される) + + +前半 13バイトでファイル項目を識別して、後半 17バイトに状態を保存する。 +1: 0~255=ソース・ファイル番号 (リカバリ・ファイルは常に 255) +4: ボリュームのシリアル番号 +8: ファイルのオブジェクトID +8: ソース・ファイルのサイズ +4: ソース・ファイルの作成日時 +4: ソース・ファイルの更新日時 +1: ファイルの状態 0=完全, 1=破損あるいはエラー, 4=追加, +16=リカバリ・ファイル + +*/ + +#define INI_VERSION 0x1270 // 検査結果の書式が決まった時のバージョン +#define REUSE_MIN 16384 // ファイル・サイズがこれより大きければ検査結果を利用する (16KB 以上 2GB 未満) +#define HEADER_SIZE 10 +#define STATE_SIZE 30 +#define STATE_READ 136 +//#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 = 同じセットの記録を削除する、今回の結果は記録する。 + 他のセットは指定された期間よりも古いものだけ削除する。 +*/ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// 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 + +// 2GB 未満のファイルの開始位置以降のハッシュ値を計算する +static unsigned int file_crc_part(HANDLE hFile) +{ + unsigned char buf[4096], *p; + unsigned int rv, crc = 0xFFFFFFFF; + unsigned int len, tmp, i; + + // 末尾まで読み込む + do { + if (!ReadFile(hFile, buf, 4096, &rv, NULL) || (rv == 0)) + break; + // CRC-32 計算 + p = buf; + len = rv; + while (len--){ + tmp = (*p++); + for (i = 0; i < 8; i++){ + if ((tmp ^ crc) & 1){ + crc = (crc >> 1) ^ 0xEDB88320; + } else { + crc = crc >> 1; + } + tmp = tmp >> 1; + } + } + } while (rv > 0); + + return crc ^ 0xFFFFFFFF; +} + +// .BIN ファイルの offset バイト目から size バイトのデータを buf に読み込む +static int file_read_bin( + int offset, + unsigned char *buf, + unsigned int size) +{ + unsigned int rv; + + // ファイルの位置を offsetバイト目にする + rv = SetFilePointer(hIniBin, offset, NULL, FILE_BEGIN); + if (rv == INVALID_SET_FILE_POINTER) + return 1; + + // size バイトを読み込む + if ((!ReadFile(hIniBin, buf, size, &rv, NULL)) || (size != rv)) + return 1; // 指定サイズを読み込めなかったらエラーになる + + return 0; +} + +// .BIN ファイルの offset バイト目に size バイトのデータを buf から書き込む +static int file_write_bin( + int offset, + unsigned char *buf, + unsigned int size) +{ + unsigned int rv; + + // ファイルの位置を offsetバイト目にする + rv = SetFilePointer(hIniBin, offset, NULL, FILE_BEGIN); + if (rv == INVALID_SET_FILE_POINTER) + return 1; + + // size バイトを書き込む + if (!WriteFile(hIniBin, buf, size, &rv, NULL)) + return 1; + + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +// 検査結果ファイルを利用する為の関数 + +// ini_path で指定されてる検査結果ファイルを削除する +static void delete_ini_file(void) +{ + // 開いてるなら閉じる + if (hIniBin != NULL){ + CloseHandle(hIniBin); + hIniBin = NULL; + } + + DeleteFile(ini_path); // .bin ファイルを削除する + + recent_data = 0; // これ以降は検査結果は利用できない +} + +// 検査するチェックサム・ファイルが同じであれば、再検査する必要は無い +int check_ini_file(unsigned char *set_hash, int block_num) +{ + 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"1_%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X.bin", + set_hash[0], set_hash[1], set_hash[2], set_hash[3], set_hash[4], set_hash[5], set_hash[6], set_hash[7], + set_hash[8], set_hash[9], set_hash[10], set_hash[11],set_hash[12], set_hash[13], set_hash[14], set_hash[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"1_*.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); +#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); +#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_FLAG_RANDOM_ACCESS, NULL); + if (hIniBin == INVALID_HANDLE_VALUE){ + hIniBin = NULL; + recent_data = 0; // 開けない場合は再利用しない + return 0; + } + //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, 4); // CRC-32 は後で書き込む + memcpy(set_data + 6, &block_num, 4); + if (match != 0){ // ID が同じでも Set 内容が異なる場合は初期化する + i = file_read_bin(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) // ソース・ブロック数を比較する + i = memcmp(set_data + 6, set_data + (HEADER_SIZE + 6), HEADER_SIZE - 6); + if (i != 0) // ID が同じでも Set 内容が異なる場合は初期化する + match = 0; + } + + // 一致しなかった場合は今回のデータを記録する + if (match == 0){ + // 今回のデータを書き込む + if (file_write_bin(0, set_data, HEADER_SIZE) != 0){ + delete_ini_file(); + return 0; + } + if (SetEndOfFile(hIniBin) == 0){ + delete_ini_file(); + return 0; + } + return 0; +#ifdef VERBOSE + } else { + time_f_date(&ft, (char *)path); + printf("Date of result: %s\n", (char *)path); +#endif + } + + return 1; +} + +// 検査結果ファイルを閉じる +void close_ini_file(void) +{ + if ((recent_data != 0) && (hIniBin != NULL)){ + unsigned int new_crc, old_crc; + + // 閉じる前に検査結果の CRC-32 を計算して記録しておく + FlushFileBuffers(hIniBin); + file_read_bin(2, (unsigned char *)&old_crc, 4); + new_crc = file_crc_part(hIniBin); + if (new_crc != old_crc) // 検査結果が同じなら更新しない + file_write_bin(2, (unsigned char *)&new_crc, 4); + CloseHandle(hIniBin); + hIniBin = NULL; + } +} + +// 検査結果が記録されてるかどうか +// -1=エラー, -2=記録なし、またはファイル項目はあるが状態が変化してる +// ファイルの状態 0=完全, 1=破損, 2=追加 +int check_ini_state( + int num, // ファイル番号 + unsigned int meta[7], // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 + HANDLE hFile) // そのファイルのハンドル +{ + unsigned char buf[STATE_SIZE * STATE_READ], buf2[STATE_SIZE]; + unsigned int rv, off; + BY_HANDLE_FILE_INFORMATION fi; + + // 現在のファイル属性を取得する + 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; + } else { + return -1; // 属性の読み取りエラー + } + + // 参照するファイル項目 + buf2[0] = (unsigned char)(num & 0xFF); + memcpy(buf2 + 1, meta + 4, 12); + memcpy(buf2 + 13, meta, 16); + buf2[29] = (unsigned char)(num >> 8); + + // ヘッダーの直後から開始する + if (SetFilePointer(hIniBin, HEADER_SIZE, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER){ + delete_ini_file(); + return -2; + } + ini_off = HEADER_SIZE; // ファイル項目の位置 + // 一度に STATE_READ 個ずつ読み込む + while (ReadFile(hIniBin, buf, STATE_SIZE * STATE_READ, &rv, NULL) != 0){ + if (rv < STATE_SIZE) + break; + // ファイル識別番号から同じファイルの記録を探す + for (off = 0; off < rv; off += STATE_SIZE){ + if ((memcmp(buf + off, buf2, 13) == 0) && ((buf[off + 29] & 0x10) == buf2[29])){ + ini_off += off; // 同じファイルの記録があった +#ifdef VERBOSE + printf("check state, num = %d, offset = %d, state = 0x%02X\n", num & 0xFF, ini_off, buf[off + 29]); +#endif + // ファイルのサイズと日時を比較する + if (memcmp(buf + (off + 13), buf2 + 13, 16) == 0) + return buf[off + 29] & 0x0F; + return -2; // 状態が変化してる + } + } + ini_off += rv; + } + + ini_off = 0; + return -2; // これ以上の検査記録は無い +} + +// ソース・ファイル状態は完全か破損だけ (消失だと検査しない) +void write_ini_state( + int num, // ファイル番号 + unsigned int meta[7], // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 + int state) // 状態、0=完全, 1=破損, 2=追加 +{ + unsigned char buf[STATE_SIZE]; + + if ((recent_data == 0) || ((meta[0] <= REUSE_MIN) && (meta[1] == 0))) + return; // 小さなファイルは検査結果を記録しない + + if (ini_off == 0){ // 記録が無ければ末尾に追加する + ini_off = GetFileSize(hIniBin, NULL); + if (ini_off == INVALID_FILE_SIZE){ + delete_ini_file(); + return; + } + } + + // 今回の状態を書き込む + buf[0] = (unsigned char)(num & 0xFF); + memcpy(buf + 1, meta + 4, 12); + memcpy(buf + 13, meta, 16); + buf[29] = (unsigned char)((state & 0x0F) | (num >> 8)); +#ifdef VERBOSE + printf("write state, num = %d, offset = %d, state = 0x%02X\n", num & 0xFF, ini_off, buf[29]); +#endif + if (file_write_bin(ini_off, buf, STATE_SIZE) != 0){ + delete_ini_file(); + return; + } +} + diff --git a/source/par1j/ini.h b/source/par1j/ini.h new file mode 100644 index 0000000..7d9d763 --- /dev/null +++ b/source/par1j/ini.h @@ -0,0 +1,32 @@ +#ifndef _INI_H_ +#define _INI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +#define INI_NAME_LEN 38 // 検査結果ファイルのファイル名の文字数 + +extern int recent_data; + +int check_ini_file(unsigned char *set_hash, int file_num); +void close_ini_file(void); +void write_ini_file(void); + +int check_ini_state( + int num, // ファイル番号 + unsigned int meta[7], // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 + HANDLE hFile); // そのファイルのハンドル + +void write_ini_state( + int num, // ファイル番号 + unsigned int meta[7], // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号 + int state); // 状態 + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par1j/md5_1.c b/source/par1j/md5_1.c new file mode 100644 index 0000000..097b5e5 --- /dev/null +++ b/source/par1j/md5_1.c @@ -0,0 +1,223 @@ +// md5_1.c +// Copyright : 2021-05-14 Yutaka Sawada +// License : GPL + +#include + +#include "common1.h" +#include "phmd5.h" +#include "md5_1.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 ハッシュ値を求める +int file_md5( + wchar_t *file_name, // 表示するファイル名 + HANDLE hFileRead, // ハッシュ値を求めるファイルのハンドル + unsigned __int64 file_size, + unsigned char *hash) // ハッシュ値 (16バイト) +{ + unsigned char buf[IO_SIZE]; + unsigned int rv, len; + unsigned int time_last; + unsigned __int64 file_left; + PHMD5 hash_ctx; + + // ファイルの先頭に戻す + file_left = 0; + if (!SetFilePointerEx(hFileRead, *((PLARGE_INTEGER)&file_left), NULL, FILE_BEGIN)) + return 1; + + prog_last = -1; // 検証中のファイル名を毎回表示する + time_last = GetTickCount(); + file_left = file_size; + Phmd5Begin(&hash_ctx); // 初期化 + + while (file_left > 0){ + if (file_left < IO_SIZE){ + len = (unsigned int)file_left; + } else { + len = IO_SIZE; + } + if (!ReadFile(hFileRead, buf, len, &rv, NULL) || (len != rv)) + return 1; + file_left -= len; + + // MD5 計算 + Phmd5Process(&hash_ctx, buf, len); + + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress_file((int)(((file_size - file_left) * 1000) / file_size), file_name)) + return 2; + time_last = GetTickCount(); + } + } + Phmd5End(&hash_ctx); // 最終処理 + memcpy(hash, hash_ctx.hash, 16); + + return 0; +} + +// ファイルの MD5 ハッシュ値を求めて、全体的な経過を表示する +int file_md5_total( + HANDLE hFileRead, // ハッシュ値を求めるファイルのハンドル + unsigned __int64 file_size, + unsigned char *hash, // ハッシュ値 (16バイト) + unsigned char *hash16, // 先頭 16KB 分のハッシュ値 + __int64 total_file_size, + __int64 *prog_now) // 経過表示での現在位置 +{ + unsigned char buf[IO_SIZE]; + unsigned int rv, len; + unsigned int time_last; + unsigned __int64 file_left; + PHMD5 hash_ctx; + + // ファイルの先頭に戻す + file_left = 0; + if (!SetFilePointerEx(hFileRead, *((PLARGE_INTEGER)&file_left), NULL, FILE_BEGIN)) + return 1; + + time_last = GetTickCount() / UPDATE_TIME; // 時刻の変化時に経過を表示する + file_left = file_size; + Phmd5Begin(&hash_ctx); // 初期化 + + while (file_left > 0){ + if (file_left < IO_SIZE){ + len = (unsigned int)file_left; + } else { + len = IO_SIZE; + } + if (!ReadFile(hFileRead, buf, len, &rv, NULL) || (len != rv)) + return 2; + (*prog_now) += len; + if (file_left == file_size){ // 最初だけ先頭 16KB 分のハッシュ値を計算する + if (len > 16384){ + Phmd5Process(&hash_ctx, buf, 16384); + Phmd5End(&hash_ctx); // 最終処理 + memcpy(hash16, hash_ctx.hash, 16); + Phmd5Begin(&hash_ctx); // 初期化しなおす + } else { // ファイル・サイズが 16KB 以下なら + Phmd5Process(&hash_ctx, buf, len); + Phmd5End(&hash_ctx); // 最終処理 + memcpy(hash, hash_ctx.hash, 16); + return 0; + } + } + file_left -= len; + + // MD5 計算 + Phmd5Process(&hash_ctx, buf, len); + + // 経過表示 + if (GetTickCount() / UPDATE_TIME != time_last){ + if (print_progress((int)(((*prog_now) * 1000) / total_file_size))) + return -2; + time_last = GetTickCount() / UPDATE_TIME; + } + } + Phmd5End(&hash_ctx); // 最終処理 + memcpy(hash, hash_ctx.hash, 16); + + return 0; +} + +// ファイルの先頭 16KB 分の MD5 ハッシュ値を求める +int file_md5_16k( + HANDLE hFileRead, // ハッシュ値を求めるファイルのハンドル + unsigned __int64 file_size, + unsigned char *hash) // ハッシュ値 (16バイト) +{ + unsigned char buf[16384]; + unsigned int len; + LARGE_INTEGER qwi; // Quad Word Integer + PHMD5 hash_ctx; + + // ファイルの先頭に戻す + qwi.QuadPart = 0; + if (!SetFilePointerEx(hFileRead, qwi, NULL, FILE_BEGIN)) + return 1; + + Phmd5Begin(&hash_ctx); // 初期化 + + if (!ReadFile(hFileRead, buf, 16384, &len, NULL)) + return 2; + if ((unsigned __int64)len > file_size) + len = (unsigned int)file_size; + + // MD5 計算 + Phmd5Process(&hash_ctx, buf, len); + Phmd5End(&hash_ctx); // 最終処理 + memcpy(hash, hash_ctx.hash, 16); + + return 0; +} + +// ファイルの先頭 32バイト目からの MD5 ハッシュ値を求める +int file_md5_from32( + wchar_t *file_name, // 表示するファイル名 + HANDLE hFileRead, // ハッシュ値を求めるファイルのハンドル + unsigned char *hash) // ハッシュ値 (16バイト) +{ + unsigned char buf[IO_SIZE]; + unsigned int rv, len; + unsigned int time_last; + unsigned __int64 file_size, file_left; + LARGE_INTEGER qwi; // Quad Word Integer + PHMD5 hash_ctx; + + // ファイル・サイズを取得する + if (!GetFileSizeEx(hFileRead, (PLARGE_INTEGER)&file_size)) + return 1; + if (file_size <= 32) + return 1; + + // ファイルの開始位置を 32バイト目にする + qwi.QuadPart = 32; + if (!SetFilePointerEx(hFileRead, qwi, NULL, FILE_BEGIN)) + return 1; + + prog_last = -1; // 検証中のファイル名を毎回表示する + time_last = GetTickCount(); + file_left = file_size - 32; + Phmd5Begin(&hash_ctx); // 初期化 + + while (file_left > 0){ + if (file_left < IO_SIZE){ + len = (unsigned int)file_left; + } else { + len = IO_SIZE; + } + if (!ReadFile(hFileRead, buf, len, &rv, NULL) || (len != rv)) + return 1; + file_left -= len; + + // MD5 計算 + Phmd5Process(&hash_ctx, buf, len); + + // 経過表示 + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress_file((int)(((file_size - file_left) * 1000) / file_size), file_name)) + return 2; + time_last = GetTickCount(); + } + } + Phmd5End(&hash_ctx); // 最終処理 + memcpy(hash, hash_ctx.hash, 16); + + return 0; +} diff --git a/source/par1j/md5_1.h b/source/par1j/md5_1.h new file mode 100644 index 0000000..26143e5 --- /dev/null +++ b/source/par1j/md5_1.h @@ -0,0 +1,48 @@ +#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 ハッシュ値を求める +int file_md5( + wchar_t *file_name, // 表示するファイル名 + HANDLE hFileRead, // ハッシュ値を求めるファイルのハンドル + unsigned __int64 file_size, + unsigned char *hash); // ハッシュ値 (16バイト) + +// ファイルの MD5 ハッシュ値を求めて、全体的な経過を表示する +int file_md5_total( + HANDLE hFileRead, // ハッシュ値を求めるファイルのハンドル + unsigned __int64 file_size, + unsigned char *hash, // ハッシュ値 (16バイト) + unsigned char *hash16, // 先頭 16KB 分のハッシュ値 + __int64 total_file_size, + __int64 *prog_now); // 経過表示での現在位置 + +// ファイルの先頭 16KB 分の MD5 ハッシュ値を求める +int file_md5_16k( + HANDLE hFileRead, // ハッシュ値を求めるファイルのハンドル + unsigned __int64 file_size, + unsigned char *hash); // ハッシュ値 (16バイト) + +// ファイルの先頭 32バイト目からの MD5 ハッシュ値を求める +int file_md5_from32( + wchar_t *file_name, // 表示するファイル名 + HANDLE hFileRead, // ハッシュ値を求めるファイルのハンドル + unsigned char *hash); // ハッシュ値 (16バイト) + + +#ifdef __cplusplus +} +#endif + +#endif // md5.h diff --git a/source/par1j/par1.c b/source/par1j/par1.c new file mode 100644 index 0000000..31b18eb --- /dev/null +++ b/source/par1j/par1.c @@ -0,0 +1,3031 @@ +// par1.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 "common1.h" +#include "phmd5.h" +#include "md5_1.h" +#include "ini.h" +#include "par1.h" +#include "version.h" + +/* +Fast Galois Field Arithmetic Library in C/C++ +Copright (C) 2007 James S. Plank +*/ +#include "gf8.h" + +/* +// 行列の表示用関数 +void galois_print_matrix(int *matrix, int rows, int cols) +{ + int i, j; + printf("\n"); + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + printf("%4d", matrix[i*cols+j]); + } + printf("\n"); + } +} +*/ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* +gflib の行列作成用関数や行列の逆変換用の関数を元にして、 +計算のやり方を PAR 1.0 用に修正する。 + +par-v1.1.tar.gz に含まれる rs.doc +Dummies guide to Reed-Solomon coding. を参考にする + +解説ではパリティ計算用の行列の要素、掛ける値は +F(i,j) = i^(j-1) ・・・ i の (j-1) 乗 +ソース・ブロックは i = 1~N で、 +パリティ・ブロックは j = 1~M になってる。 + +実際に計算する関数では +ソース・ブロックが i = 0 ~ cols - 1 で、 +パリティ・ブロックが j = 0 ~ rows - 1 なので、 + +理論のF(i,j) = F(i+1, j+1) = (i+1) ^ (j+1-1) = (i+1) ^ j +・・・ (i+1) の j 乗 +*/ + +// PAR 1.0 のパリティ作成用の行列 +int * make_encode_matrix( + int rows, // 横行、パリティのブロック数 + int cols, // 縦列、ソースのブロック数 + int first) // 最初のパリティ・ブロック番号 +{ + int *mat, i, j; + + mat = malloc(sizeof(int) * rows * cols); + if (mat == NULL){ + printf("malloc, %d\n", sizeof(int) * rows * cols); + return NULL; + } + + for (j = 0; j < rows; j++){ + for (i = 0; i < cols; i++) + mat[cols * j + i] = galois_power(i + 1, first + j); + } + + return mat; +} + +/* +失われたソース・ブロックの数 = 利用するパリティ・ブロックの数を縦行とし、 +ソース・ブロックの数を横列とする長方形の行列を作る。 + +例えば、ソース・ブロックを A, B, C, D, E 、 +そのパリティ・ブロックを X, Y, Z とする。 +その場合の関係式は次のようになる。 + +1: 1*A xor 1*B xor 1*C xor 1*D xor 1*E = X +2: 1*A xor 2*B xor 3*C xor 4*D xor 5*E = Y +3: 1*A xor 4*B xor 5*C xor 16*D xor 17*E = Z + +これは PARファイルを作成する際の行列と同じ。 + 1 1 1 1 1 + 1 2 3 4 5 + 1 4 5 16 17 + +ここで、B, C, D の三ブロックが失われて、それぞれを X, Y, Z で補うなら、 +その状態は次のような関係式になる。 + +1: 1*A xor 1*B xor 1*C xor 1*D xor 1*E = 0*A xor 1*X xor 0*Y xor 0*Z xor 0*E +2: 1*A xor 2*B xor 3*C xor 4*D xor 5*E = 0*A xor 0*X xor 1*Y xor 0*Z xor 0*E +3: 1*A xor 4*B xor 5*C xor 16*D xor 17*E = 0*A xor 0*X xor 0*Y xor 1*Z xor 0*E + +左:パリティ・ブロックの係数 + 1 1 1 1 1 + 1 2 3 4 5 + 1 4 5 16 17 + +右:そのパリティ・ブロックがどのソース・ブロックの位置にくるか + 0 1 0 0 0 + 0 0 1 0 0 + 0 0 0 1 0 + +ここでまず、存在するソース・ブロックの列を 0にする。 + +1: 0*A xor 1*B xor 1*C xor 1*D xor 0*E = 1*A xor 1*X xor 0*Y xor 0*Z xor 1*E +2: 0*A xor 2*B xor 3*C xor 4*D xor 0*E = 1*A xor 0*X xor 1*Y xor 0*Z xor 5*E +3: 0*A xor 4*B xor 5*C xor 16*D xor 0*E = 1*A xor 0*X xor 0*Y xor 1*Z xor 17*E + +左:ソース・ブロックが存在した所が 0になる + 0 1 1 1 0 + 0 2 3 4 0 + 0 4 5 16 0 + +右:逆にこちらではソース・ブロックが存在した所が反転する + 1 1 0 0 1 + 1 0 1 0 5 + 1 0 0 1 17 + +Gaussian Elimination (ガウスの消去法) で元の左の行列を右のに変換する + +各行で 0でない値を探して、それが 1になるように、何倍かする。 +まずは 1行目: + +1: 0*A xor 1*B xor 1*C xor 1*D xor 0*E = 1*A xor 1*X xor 0*Y xor 0*Z xor 1*E +1 行目の 2列目は 1なのでそのまま。 + +他の行の同じ列に 0でない値があれば、それを 0にするために、 +行を何倍かしたものを XOR する + +2: 0*A xor 2*B xor 3*C xor 4*D xor 0*E = 1*A xor 0*X xor 1*Y xor 0*Z xor 5*E +2 行目の 2列目は 2なので、1行目の各値を 2倍したものを XOR する。 + +3: 0*A xor 4*B xor 5*C xor 16*D xor 0*E = 1*A xor 0*X xor 0*Y xor 1*Z xor 17*E +3 行目の 2列目は 4なので、1行目の各値を 4倍したものを XOR する。 + +1: 0*A xor 1*B xor 1*C xor 1*D xor 0*E = 1*A xor 1*X xor 0*Y xor 0*Z xor 1*E +2: 0*A xor 0*B xor 1*C xor 6*D xor 0*E = 3*A xor 2*X xor 1*Y xor 0*Z xor 7*E +3: 0*A xor 0*B xor 1*C xor 20*D xor 0*E = 5*A xor 4*X xor 0*Y xor 1*Z xor 21*E + +次は 2行目: + +2: 0*A xor 0*B xor 1*C xor 6*D xor 0*E = 3*A xor 2*X xor 1*Y xor 0*Z xor 7*E +2 行目の 3列目は 1なのでそのまま。 + +1: 0*A xor 1*B xor 1*C xor 1*D xor 0*E = 1*A xor 1*X xor 0*Y xor 0*Z xor 1*E +1 行目の 3列目は 1なので、1行目の各値をそのまま XOR する。 + +3: 0*A xor 0*B xor 1*C xor 20*D xor 0*E = 5*A xor 4*X xor 0*Y xor 1*Z xor 21*E +3 行目の 3列目は 1なので、1行目の各値をそのまま XOR する。 + +1: 0*A xor 1*B xor 0*C xor 7*D xor 0*E = 2*A xor 3*X xor 1*Y xor 0*Z xor 6*E +2: 0*A xor 0*B xor 1*C xor 6*D xor 0*E = 3*A xor 2*X xor 1*Y xor 0*Z xor 7*E +3: 0*A xor 0*B xor 0*C xor 18*D xor 0*E = 6*A xor 6*X xor 1*Y xor 1*Z xor 18*E + +次は 3行目: + +3: 0*A xor 0*B xor 0*C xor 18*D xor 0*E = 6*A xor 6*X xor 1*Y xor 1*Z xor 18*E +3 行目の 4列目は 18なので、18で割る + +1: 0*A xor 1*B xor 0*C xor 7*D xor 0*E = 2*A xor 3*X xor 1*Y xor 0*Z xor 6*E +1 行目の 4列目は 7なので、1行目の各値を 7倍したものを XOR する。 + +2: 0*A xor 0*B xor 1*C xor 6*D xor 0*E = 3*A xor 2*X xor 1*Y xor 0*Z xor 7*E +2 行目の 4列目は 6なので、1行目の各値を 6倍したものを XOR する。 + +1: 0*A xor 1*B xor 0*C xor 0*D xor 0*E = 3*A xor 2*X xor 123*Y xor 122*Z xor 1*E +2: 0*A xor 0*B xor 1*C xor 0*D xor 0*E = 184*A xor 185*X xor 187*Y xor 186*Z xor 1*E +3: 0*A xor 0*B xor 0*C xor 1*D xor 0*E = 186*A xor 186*X xor 192*Y xor 192*Z xor 1*E + +左:元の右の行列と同じになってる + 0 1 0 0 0 + 0 0 1 0 0 + 0 0 0 1 0 + +右:これが失われたソース・ブロックを復元するための行列となる + 3 2 123 122 1 + 184 185 187 186 1 + 186 186 192 192 1 +*/ + +// 復元用の行列を作る、十分な数のパリティ・ブロックが必要 +int * make_decode_matrix( + int parity_max, // 本来のパリティ・ブロックの数 + int rows, // 横行、行列の縦サイズ、失われたソース・ブロックの数 = 利用するパリティ・ブロック数 + int cols, // 縦列、行列の横サイズ、本来のソース・ブロック数 + int *exist, // どのブロックが存在するか + int *id) // 失われたソース・ブロックをどのパリティ・ブロックで代用したか +{ + int *mat_max, *mat_l, *mat_r; + int factor, row_start, row_start2, col_find, i, j, k; + + // 失われたソース・ブロックをどのパリティ・ブロックで代用するか + j = 0; + for (i = 0; i < cols; i++){ + id[i] = i; + if (exist[i] == 0){ // ソース・ブロックが存在しない所 + while (j < parity_max){ + if (exist[cols + j] != 0){ // その番号のパリティ・ブロックが存在すれば + id[i] = cols + j; + j++; + break; + } else { + j++; + } + } + if (id[i] < cols){ + printf("\nneed more parity volume\n"); + return NULL; // パリティ・ブロックの数が足りなければ + } + } + } + + // 全てのパリティ・ブロックが存在すると仮定した行列を作る + mat_max = make_encode_matrix(parity_max, cols, 0); + if (mat_max == NULL) + return NULL; + + // 存在して利用するパリティ・ブロックだけの行列を作る + mat_l = malloc(sizeof(int) * rows * cols); + if (mat_l == NULL) { + free(mat_max); + printf("malloc, %d\n", sizeof(int) * rows * cols); + return NULL; + } + j = 0; + for (i = 0; i < cols; i++){ + if (id[i] >= cols){ + for (k = 0; k < cols; k++) + mat_l[cols * j + k] = mat_max[cols * (id[i] - cols) + k]; + j++; + } + } + free(mat_max); + //printf("\nLeft :\n"); + //galois_print_matrix(mat_l, rows, cols); + + // 失われたソース・ブロックと代用するパリティ・ブロックの位置を表す行列を作る + mat_r = (int *)calloc(rows * cols, sizeof(int)); + if (mat_r == NULL){ + free(mat_l); + printf("calloc, %d\n", sizeof(int) * rows * cols); + return NULL; + } + j = 0; + for (i = 0; i < cols; i++){ + if (id[i] >= cols){ + // j = 行番号 + // id[i] = その行を補ったパリティ・ブロックの番号 + // i = その行のソース・ブロック番号 + mat_r[cols * j + i] = 1; + j++; + } + } + //printf("\nRight :\n"); + //galois_print_matrix(mat_r, rows, cols); + + // mat_l の存在するソース・ブロックの列を 0 にするような値で XOR する + for (i = 0; i < cols; i++){ + if (exist[i] != 0){ // ソース・ブロックが存在する列 + for (j = 0; j < rows; j++){ // 行ごとに + factor = mat_l[cols * j + i]; // j 行の i 列の値 + mat_l[cols * j + i] = 0; // mat_l[cols * j + i] ^= factor; と同じ + mat_r[cols * j + i] ^= factor; + } + } + } + //printf("\nErase source block :\n"); + //galois_print_matrix(mat_l, rows, cols); + //printf("\n"); + //galois_print_matrix(mat_r, rows, cols); + + // Gaussian Elimination + for (j = 0; j < rows; j++){ + row_start = cols * j; // その行の開始位置 + + // mat_l の各行ごとに最初の 0でない値を探す + for (col_find = 0; col_find < cols; col_find++){ + if (mat_l[row_start + col_find] != 0) + break; + } + if (col_find == cols){ // 見つからなければ、その行列の逆行列を計算できない + printf("\nmatrix is not invertible\n"); + k = 0; + for (i = 0; i < cols; i++){ + if (id[i] >= cols){ + if (k == j){ + printf(" parity volume %u is useless\n", (1 + id[i] - cols)); + break; + } + k++; + } + } + free(mat_l); + free(mat_r); + return NULL; + } + factor = mat_l[row_start + col_find]; // col_find 列に 0 ではない値を発見 + if (factor != 1){ // factor が 1でなければ、1にする為に factor で割る + for (k = 0; k < cols; k++){ + mat_l[row_start + k] = galois_multtable_divide(mat_l[row_start + k], factor); + mat_r[row_start + k] = galois_multtable_divide(mat_r[row_start + k], factor); + } + } + + // 別の行の同じ col_find 列が 0以外なら、その値を 0にするために、 + // j 行を何倍かしたものを XOR する + for (i = 0; i < rows; i++){ + if (i == j) + continue; // 同じ行はとばす + row_start2 = cols * i; // その行の開始位置 + factor = mat_l[row_start2 + col_find]; // i 行の col_find 列の値 + if (factor != 0){ // 0でなければ + // 先の計算により、j 行の col_find 列の値は必ず 1なので、この factor が倍率になる + if (factor == 1){ // 倍率が 1なら、単純に XOR するだけ + for (k = 0; k < cols; k++){ + mat_l[row_start2 + k] ^= mat_l[row_start + k]; + mat_r[row_start2 + k] ^= mat_r[row_start + k]; + } + } else { + for (k = 0; k < cols; k++){ + mat_l[row_start2 + k] ^= galois_multtable_multiply(mat_l[row_start + k], factor); + mat_r[row_start2 + k] ^= galois_multtable_multiply(mat_r[row_start + k], factor); + } + } + } + } + + //printf("\nDivide & Eliminate :\n"); + //galois_print_matrix(mat_l, rows, cols); + //printf("\n"); + //galois_print_matrix(mat_r, rows, cols); + } + + free(mat_l); + return mat_r; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define CHUNK_SIZE 65536 + +typedef struct { + HANDLE hFile; // ファイル・ハンドル + __int64 size; // ファイルの残りサイズ +} file_ctx; + +// リード・ソロモン符号を使ってエンコードする +int rs_encode( + int source_num, // ソース・ブロックの数 + __int64 block_size, // ブロック・サイズ (最大ファイル・サイズ) + int parity_num, // パリティ・ブロックの数 + int first_num, // 最初のパリティ・ブロック番号 + file_ctx *files, + HANDLE *par_hFile, + PHMD5 *par_md5) +{ + unsigned char *buffer, *block = NULL; + int err = 0, i, j; + int *mat = NULL, factor; + unsigned int io_size, unit_size, len, rv; + unsigned int k, offset, length, chunk_count; + unsigned int time_last, prog_num = 0, prog_base; + __int64 block_left; +//unsigned int time1; + + // 利用できるメモリー量を調べる + io_size = get_mem_size(block_size * (__int64)(source_num + parity_num)); + if (io_size > 1048576 * 2) + io_size -= (parity_num * source_num) * 4 + 1048576; // 行列に必要なメモリーとスタック用に 1MB + if (io_size < 1048576) + io_size = 1048576; + io_size /= source_num + 1; // 何個分必要か + + // ブロック・サイズより大きい、またはブロック・サイズ自体が小さい場合は + if (((__int64)io_size >= block_size) || (block_size <= 4096)){ + io_size = (unsigned int)block_size; // ブロック・サイズと同じにする + } else { // ブロック・サイズを 2の乗数サイズの断片に分割する + // 断片化する場合でもブロック数が多いと 256 * 4096 = 1MB は使う + __int64 fragment_size = 4096; // 最低サイズ + while (fragment_size * 2 <= (__int64)io_size) + fragment_size *= 2; + io_size = (unsigned int)fragment_size; + } + io_size = (io_size + 3) & 0xFFFFFFFC; // 4の倍数にする + unit_size = io_size + HASH_SIZE; // チェックサムの分だけ増やす + prog_base = (unsigned int)((block_size + (__int64)io_size - 1) / (__int64)io_size) * parity_num; // 全体の断片の個数 + //printf("io_size = %d, prog_base = %d\n", io_size, prog_base); + + block_left = block_size; + if (galois_create_mult_tables() < 0){ + printf("galois_create_mult_tables\n"); + return 1; + } + + // 作業バッファーを確保する + block = malloc(unit_size * (source_num + 1)); + if (block == NULL){ + printf("malloc, %d\n", unit_size * (source_num + 1)); + err = 1; + goto error_end; + } + buffer = block + unit_size; + + // パリティ計算用の行列演算の準備をする + mat = make_encode_matrix(parity_num, source_num, first_num); // 変換行列 + if (mat == NULL){ + err = 1; + goto error_end; + } + //galois_print_matrix(mat, parity_num, source_num); + + // chunk がキャッシュに収まるようにすれば速くなる! (ストリップマイニングという最適化手法) + // CPU L2キャッシュ・サイズが 256KB として、1/4 なら 64KB + // chunk サイズが 2**16 なので分割数は 1~65536 の範囲になる + chunk_count = (unit_size + CHUNK_SIZE - 1) / CHUNK_SIZE; + //printf("split count = %d, chunk size = %d\n", chunk_count, CHUNK_SIZE); + +//time1 = GetTickCount(); + // バッファー・サイズごとにパリティ・ブロックを作成する + time_last = GetTickCount(); + while (block_left > 0){ + // バッファーにソース・ファイルの内容を読み込む + for (i = 0; i < source_num; i++){ + if (files[i].size > 0){ + if (files[i].size < io_size){ + len = (unsigned int)(files[i].size); + } else { + len = io_size; + } + files[i].size -= len; + if (!ReadFile(files[i].hFile, buffer + (unit_size * i), len, &rv, NULL) || (len != rv)){ + print_win32_err(); + printf("ReadFile, data file %d\n", i); + err = 1; + goto error_end; + } + if (len < io_size) + memset(buffer + (unit_size * i + len), 0, io_size - len); + // ソース・ブロックのチェックサムを計算する + checksum4(buffer + (unit_size * i), buffer + (unit_size * i + io_size), io_size); + } else { + // ソース・ブロックの値が全て 0 なら、チェックサムも 0 になる。 + memset(buffer + (unit_size * i), 0, unit_size); + } + } + // バッファーに読み込んだサイズ + if (block_left < io_size){ + len = (unsigned int)block_left; + } else { + len = io_size; + } + block_left -= len; + + // パリティ・ブロックごとに + for (i = 0; i < parity_num; i++){ + /* without strip mining + memset(block, 0, unit_size); // パリティ・ブロックを 0で埋める + // ソース・ブロックごとにパリティを追加していく + for (j = 0; j < source_num; j++){ + factor = mat[i * source_num + j]; + if (factor == 1){ + galois_region_xor(buffer + (unit_size * j), block, unit_size); + } else if (factor != 0){ + galois_region_multiply(buffer + (unit_size * j), block, unit_size, factor); + } + } + */ + length = CHUNK_SIZE; + for (k = 0; k < chunk_count; k++){ // chunk の番号 + offset = k * CHUNK_SIZE; + if (offset + length >= unit_size){ // 最後の chunk なら + length = unit_size - offset; + k = chunk_count; + } + + memset(block + offset, 0, length); // パリティ・ブロックを 0で埋める + // ソース・ブロックごとにパリティを追加していく + for (j = 0; j < source_num; j++){ + factor = mat[i * source_num + j]; + if (factor == 1){ + galois_region_xor(buffer + (unit_size * j + offset), block + offset, length); + } else if (factor != 0){ + galois_region_multiply(buffer + (unit_size * j + offset), block + offset, length, factor); + } + } + } + + // 経過表示 + prog_num++; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((prog_num * 1000) / prog_base)){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + + // パリティ・ブロックのチェックサムを検証する + checksum4(block, (unsigned char *)&rv, io_size); + if (memcmp(block + io_size, &rv, HASH_SIZE) != 0){ + printf("checksum mismatch, parity volume %d\n", i); + err = 1; + goto error_end; + } + Phmd5Process(&(par_md5[i]), block, len); // ハッシュ値を計算する + // リカバリ・ファイルへ書き込む + if (!WriteFile(par_hFile[i], block, len, &rv, NULL)){ + print_win32_err(); + printf("WriteFile, parity volume %d\n", first_num + 1 + i); + err = 1; + goto error_end; + } + } + } + print_progress_done(); // 改行して行の先頭に戻しておく +//time1 = GetTickCount() - time1; +//printf("encode %u.%03u sec\n", time1 / 1000, time1 % 1000); + +error_end: + if (block) + free(block); + if (mat) + free(mat); + // gflib のテーブルを解放する + galois_free_tables(); + return err; +} + +// リード・ソロモン符号を使ってデコードする +int rs_decode( + int source_num, // 本来のソース・ブロックの数 + int block_lost, // 失われたソース・ブロックの数 + __int64 block_size, // ブロック・サイズ (最大ファイル・サイズ) + int parity_max, // 本来のパリティ・ブロックの数 + int *exist, // そのブロックが存在するか + file_ctx *files) +{ + unsigned char *buffer, *block = NULL; + int err = 0, i, j; + int *mat = NULL, *id = NULL, factor; + int block_recover; + unsigned int io_size, unit_size, len, rv, len2; + unsigned int k, offset, length, chunk_count; + unsigned int time_last, prog_num = 0, prog_base; + __int64 block_left; + + // 利用できるメモリー量を調べる + io_size = get_mem_size(block_size * (__int64)(source_num + block_lost)); + io_size -= (block_lost * source_num * 8) + (parity_max * source_num * 4) + (source_num * 4); + io_size -= 1048576; // 行列に必要なメモリーとスタック用に 1MB + if (io_size < 1048576) + io_size = 1048576; + io_size /= source_num + 1; // 何個分必要か + + // ブロック・サイズより大きい、またはブロック・サイズ自体が小さい場合は + if (((__int64)io_size >= block_size) || (block_size <= 4096)){ + io_size = (unsigned int)block_size; // ブロック・サイズと同じにする + } else { // ブロック・サイズを 2の乗数サイズの断片に分割する + // 断片化する場合でもブロック数が多いと 256 * 4096 = 1MB は使う + __int64 fragment_size = 4096; // 最低サイズ + while (fragment_size * 2 <= (__int64)io_size) + fragment_size *= 2; + io_size = (unsigned int)fragment_size; + } + io_size = (io_size + 3) & 0xFFFFFFFC; // 4の倍数にする + unit_size = io_size + HASH_SIZE; // チェックサムの分だけ増やす + prog_base = (unsigned int)((block_size + (__int64)io_size - 1) / (__int64)io_size) * block_lost; // 全体の断片の個数 + //printf("io_size = %d\n", io_size); + + block_left = block_size; + if (galois_create_mult_tables() < 0){ + printf("galois_create_mult_tables\n"); + return 1; + } + + // 作業バッファーを確保する + block = malloc(unit_size * (source_num + 1)); + if (block == NULL){ + printf("malloc, %d\n", unit_size * (source_num + 1)); + err = 1; + goto error_end; + } + buffer = block + unit_size; + + // パリティ計算用の行列演算の準備をする + id = malloc(sizeof(int) * source_num); + if (id == NULL){ + printf("malloc, %d\n", sizeof(int) * source_num); + err = 1; + goto error_end; + } + + mat = make_decode_matrix(parity_max, block_lost, source_num, exist, id); + if (mat == NULL){ + err = 1; + goto error_end; + } + //galois_print_matrix(mat, block_lost, source_num); + //for (i = 0; i < source_num; i++) + // printf("id[%d] = %d\n", i, id[i]); + + // chunk サイズが 2**16 なので分割数は 1~65536 の範囲になる + chunk_count = (unit_size + CHUNK_SIZE - 1) / CHUNK_SIZE; + + // バッファー・サイズごとにソース・ブロックを復元する + time_last = GetTickCount(); + while (block_left > 0){ + // バッファーにソース・ファイルとリカバリ・ファイルの内容を読み込む + for (i = 0; i < source_num; i++){ + //printf("%d: id = %d, exist = %d, size = %I64u \n", i, id[i], exist[id[i]], files[id[i]].size); + if (files[id[i]].size > 0){ + if (files[id[i]].size < io_size){ + len = (unsigned int)(files[id[i]].size); + } else { + len = io_size; + } + files[id[i]].size -= len; + if (!ReadFile(files[id[i]].hFile, buffer + (unit_size * i), len, &rv, NULL)){ + print_win32_err(); + printf("ReadFile, data file %d\n", id[i]); + err = 1; + goto error_end; + } else if (len != rv){ + printf("ReadFile, data file %d, %d, %d\n", id[i], len, rv); + err = 1; + goto error_end; + } + if (len < io_size) + memset(buffer + (unit_size * i + len), 0, io_size - len); + // ソース・ブロックのチェックサムを計算する + checksum4(buffer + (unit_size * i), buffer + (unit_size * i + io_size), io_size); + } else { + // ソース・ブロックの値が全て 0 なら、チェックサムも 0 になる。 + memset(buffer + (unit_size * i), 0, unit_size); + } + } + // バッファーに読み込んだサイズ + if (block_left < io_size){ + len = (unsigned int)block_left; + } else { + len = io_size; + } + block_left -= len; + + // 失われたソース・ブロックごとに + block_recover = 0; + for (i = 0; i < source_num; i++){ + if (id[i] >= source_num){ // パリティ・ブロックで補った部分 + /* without strip mining + memset(block, 0, unit_size); // ソース・ブロックを 0で埋める + // 失われたソース・ブロックを復元していく + for (j = 0; j < source_num; j++) { + factor = mat[source_num * block_recover + j]; + if (factor == 1){ + galois_region_xor(buffer + (unit_size * j), block, unit_size); + } else if (factor != 0){ + galois_region_multiply(buffer + (unit_size * j), block, unit_size, factor); + } + } + */ + length = CHUNK_SIZE; + for (k = 0; k < chunk_count; k++){ // chunk の番号 + offset = k * CHUNK_SIZE; + if (offset + length >= unit_size){ // 最後の chunk なら + length = unit_size - offset; + k = chunk_count; + } + + memset(block + offset, 0, length); // ソース・ブロックを 0で埋める + // 失われたソース・ブロックを復元していく + for (j = 0; j < source_num; j++) { + factor = mat[source_num * block_recover + j]; + if (factor == 1){ + galois_region_xor(buffer + (unit_size * j + offset), block + offset, length); + } else if (factor != 0){ + galois_region_multiply(buffer + (unit_size * j + offset), block + offset, length, factor); + } + } + } + + // 経過表示 + prog_num++; + if (GetTickCount() - time_last >= UPDATE_TIME){ + if (print_progress((prog_num * 1000) / prog_base)){ + err = 2; + goto error_end; + } + time_last = GetTickCount(); + } + + // 復元されたソース・ブロックのチェックサムを検証する + checksum4(block, (unsigned char *)&rv, io_size); + if (memcmp(block + io_size, &rv, HASH_SIZE) != 0){ + printf("checksum mismatch, recovered data file %d\n", i); + err = 1; + goto error_end; + } + block_recover++; + // ソース・ファイルに書き込むサイズ + if (files[i].size < len){ + len2 = (unsigned int)(files[i].size); + } else { + len2 = len; + } + files[i].size -= len2; + // ソース・ファイルへ書き込む + if (!WriteFile(files[i].hFile, block, len2, &rv, NULL)){ + print_win32_err(); + printf("WriteFile, data file %d\n", i); + err = 1; + goto error_end; + } + } + } + } + print_progress_done(); // 改行して行の先頭に戻しておく + +error_end: + if (block) + free(block); + if (id) + free(id); + if (mat) + free(mat); + // gflib のテーブルを解放する + galois_free_tables(); + return err; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// PAR 1.0 のヘッダーを作成する +int set_header( + unsigned char *buf, + file_ctx *files, // ソース・ブロックのハンドルとサイズ + int source_num, // ソース・ブロックの数 + int file_num, // ソース・ファイルの数 + wchar_t *list_buf, // ソース・ファイルのリスト + int list_len) // ファイル・リストの文字数 +{ + unsigned char hash[16], hash16[16], set_hash[16 * 256]; + unsigned char hash0[16] = {0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, + 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e}; + wchar_t file_path[MAX_LEN], *file_name; + int i, off, list_off, len, num = 0; + __int64 file_size, total_file_size, prog_now; + WIN32_FILE_ATTRIBUTE_DATA AttrData; + + memset(buf, 0, 96); + // Identification String + buf[0] = 'P'; + buf[1] = 'A'; + buf[2] = 'R'; + // Version Number + i = 0x00010000; // PAR 1.0 + memcpy(buf + 8, &i, 4); + // "program"-"version"-"subversion"-"subsubversion". + buf[12] = PRODUCT_VERSION & 0xF; // subsubversion + buf[13] = (PRODUCT_VERSION >> 4) & 0xF; // subversion + buf[14] = PRODUCT_VERSION >> 8; // version, ちなみに par-cmdline の最終バージョンは 0.9.0 ? + buf[15] = 2; // program, 1=Mirror, 2=PAR, 3=SmartPar, 0&4~=Unknown client + // control hash の 16バイトは最後に書き込む + // set hash の 16バイトは後で書き込む + // volume number はパリティ・ブロックごとに変更する + // number of files + memcpy(buf + 56, &file_num, 4); + // start offset of the file list + buf[64] = 0x60; + // file list size の 8バイトは後で書き込む + // start offset of the data の 8バイトは後で書き込む + // data size はパリティ・ブロックならブロック・サイズを書き込む + + // 合計ファイル・サイズを求める + total_file_size = 0; + prog_now = 0; + list_off = 0; + while (list_off < list_len){ + // ファイル名を取得する + file_name = list_buf + list_off; // サブ・ディレクトリを含まないのでそのまま使う + list_off += wcslen(file_name) + 1; + + wcscpy(file_path, base_dir); + wcscat(file_path, file_name); + if (!GetFileAttributesEx(file_path, GetFileExInfoStandard, &AttrData)){ + print_win32_err(); + printf_cp("cannot open input file, %s\n", file_name); + return 0; + } + file_size = ((__int64)(AttrData.nFileSizeHigh) << 32) | (__int64)(AttrData.nFileSizeLow); + total_file_size += file_size; + } + + // ここから file list + off = 0x0060; + list_off = 0; + while (list_off < list_len){ + // ファイル名を取得する + file_name = list_buf + list_off; // サブ・ディレクトリを含まないのでそのまま使う + len = wcslen(file_name); + list_off += (len + 1); + len *= 2; // 文字数からバイト数にする + + // entry size + i = 0x38 + len; + memcpy(buf + off, &i, 4); + memset(buf + off + 4, 0, 4); + off += 8; + // status field + wcscpy(file_path, base_dir); + wcscat(file_path, file_name); + if (!GetFileAttributesEx(file_path, GetFileExInfoStandard, &AttrData)){ + print_win32_err(); + printf_cp("cannot open input file, %s\n", file_name); + return 0; + } + file_size = ((__int64)(AttrData.nFileSizeHigh) << 32) | (__int64)(AttrData.nFileSizeLow); + if (file_size > 0){ + i = 1; + } else { + i = 0; // 空のファイルはパリティ計算に含めない + } + memcpy(buf + off, &i, 4); + memset(buf + off + 4, 0, 4); + off += 8; + // size + memcpy(buf + off, &file_size, 8); + off += 8; + // MD5 hash + if (file_size > 0){ + // パリティ・ブロック計算用に開く + files[num].size = file_size; + files[num].hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (files[num].hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + printf_cp("cannot open input file, %s\n", file_name); + return 0; + } + // MD5 を計算する + i = file_md5_total(files[num].hFile, file_size, hash, hash16, total_file_size, &prog_now); + if (i != 0){ + if (i < 0) + return i; + printf_cp("file_md5_total, %s\n", file_name); + return 0; + } + } else { + memcpy(hash, hash0, 16); + } + memcpy(buf + off, hash, 16); + off += 16; + if (file_size > 0) + memcpy(set_hash + (num * 16), hash, 16); + // 16k MD5 hash + if (file_size > 16384) + memcpy(hash, hash16, 16); + memcpy(buf + off, hash, 16); + off += 16; + // filename + memcpy(buf + off, file_name, len); + off += len; + + if (file_size > 0) + num++; + } + + // set hash をここで書き込む + data_md5(set_hash, source_num * 16, hash); + memcpy(buf + 32, hash, 16); + // file list size をここで書き込む + i = off - 96; + memcpy(buf + 72, &i, 4); + // start offset of the data をここで書き込む + memcpy(buf + 80, &off, 4); + + // 最後にファイルのハッシュ値を計算する + data_md5(buf + 32, off - 32, hash); + memcpy(buf + 16, hash, 16); + + return off; +} + +// インデックス・ファイルを作り直す +int recreate_par( + wchar_t *file_path, // PAR ファイルのパス + unsigned char *pxx_buf, // PXX ファイルのヘッダー + unsigned char *buf) // 作業バッファー +{ + unsigned char hash[16]; + int data_off, rv; + HANDLE hFileWrite; + + // start offset of the data を読み取る + memcpy(&data_off, pxx_buf + 80, 4); + memcpy(buf, pxx_buf, data_off); // ヘッダー部分をコピーする + + // volume number を 0にする + memset(buf + 48, 0, 8); + // data size を 0にする + memset(buf + 88, 0, 8); + // control hash を計算しなおす + data_md5(buf + 32, data_off - 32, hash); + memcpy(buf + 16, hash, 16); + + // ファイルに書き込む + hFileWrite = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFileWrite == INVALID_HANDLE_VALUE) + return 1; + if (!WriteFile(hFileWrite, buf, data_off, &rv, NULL)){ + CloseHandle(hFileWrite); + return 1; + } + CloseHandle(hFileWrite); + return 0; +} + +// PAR 1.0 のヘッダーから値を読み取る +int get_header( + wchar_t *file_name, + HANDLE hFile, + unsigned char *buf, + int buf_size, + unsigned char *set_hash, // NULL でなければ set hash、同じ構成の PXX かどうかを識別するため + int *volume_num, // リカバリ・ファイルの番号 + int *file_num, // ソース・ファイルの数 + int *list_off, // ソース・ファイルのリストの開始位置 + int *list_size, // ソース・ファイルのリストのサイズ + int *data_off, // ソースの開始位置 + __int64 *data_size) // ソースのサイズ +{ + unsigned char hash[16]; + int i; + unsigned int rv, meta_data[7]; + + if (buf_size <= 96) + return 1; + + // ファイルの先頭に戻す + if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER){ + print_win32_err(); + return 1; + } + + // ヘッダー部分を読み込む + if (!ReadFile(hFile, buf, buf_size, &rv, NULL)){ + print_win32_err(); + printf("ReadFile, header\n"); + return 1; + } + + // Identification String + memcpy(&i, buf, 4); + if (i != 5390672) // 'P' + 'A' * 256 + 'R' * 65536 + return 1; + memcpy(&i, buf + 4, 4); + if (i != 0) + return 1; + // Version Number + memcpy(&i, buf + 8, 4); + if ((i & 0xffff0000) != 0x00010000) // PAR v1.** にだけ対応する + return 1; + + // 完全なリカバリ・ファイルを見つけた後だけ、検査結果を探す + if (list_size == NULL){ + i = check_ini_state(0x10FF, meta_data, hFile); + if (i == -2){ // 記録が無い、または状態が変化してる + // control hash + if (rv = file_md5_from32(file_name, hFile, hash)) + return rv; + i = 0; + if (memcmp(hash, buf + 16, 16) != 0) + i = 1; + // 今回の検査結果を書き込む + write_ini_state(0x10FF, meta_data, i); + } + if (i != 0) + return 1; + + } else { + // control hash + if (rv = file_md5_from32(file_name, hFile, hash)) + return rv; + if (memcmp(hash, buf + 16, 16) != 0) + return 1; + } + + // set hash + if (set_hash != NULL) + memcpy(set_hash, buf + 32, 16); + // volume number + if (volume_num != NULL) + memcpy(volume_num, buf + 48, 4); + // number of files + if (file_num != NULL) + memcpy(file_num, buf + 56, 4); + // start offset of the file list + if (list_off != NULL) + memcpy(list_off, buf + 64, 4); + // file list size + if (list_size != NULL) + memcpy(list_size, buf + 72, 4); + // start offset of the data + if (data_off != NULL) + memcpy(data_off, buf + 80, 4); + // data size + if (data_size != NULL) + memcpy(data_size, buf + 88, 8); + + return 0; +} + +// ファイル・リストからブロック数を読み取る +int get_source_num( + unsigned char *buf, + __int64 *block_size) +{ + int i, off, file_num, entry_size, status, source_num = 0; + __int64 file_size; + + // number of files + memcpy(&file_num, buf + 56, 4); + // start offset of the file list + memcpy(&off, buf + 64, 4); + + for (i = 0; i < file_num; i++){ + // entry size + memcpy(&entry_size, buf + off, 4); + // status field + memcpy(&status, buf + (off + 8), 4); + if ((status & 1) == 1){ + source_num++; + // file size + memcpy(&file_size, buf + (off + 16), 8); + if (file_size > *block_size) + *block_size = file_size; + } + + off += entry_size; + } + + printf("%3d \r", source_num); + return source_num; +} + +// コメントを読み取る +static void read_comment( + wchar_t *par_comment, + unsigned char *buf, + int len) +{ + if (par_comment[0] != 0) + return; + + if (len >= COMMENT_LEN * 2) + len = (COMMENT_LEN - 1) * 2; + memcpy(par_comment, buf, len); + len /= 2; // 文字数にする + par_comment[len] = 0; // 末尾を null 文字にする + for (len = 0; len < COMMENT_LEN; len++){ // 表示する前に sanitalize する + if (par_comment[len] == 0){ + break; + } else if ((par_comment[len] <= 31) || (par_comment[len] == 127)){ // 制御文字を消す + par_comment[len] = ' '; + } + } +} + +// リカバリ・ファイルを作成する +int par1_create( + int switch_p, // インデックス・ファイルを作らない + int source_num, // ソース・ブロックの数 + __int64 block_size, // ブロック・サイズ (最大ファイル・サイズ) + int parity_num, // パリティ・ブロックの数 + int first_vol, // 最初のリカバリ・ファイル番号 + int file_num, // ソース・ファイルの数 + wchar_t *list_buf, // ソース・ファイルのリスト + int list_len, // ファイル・リストの文字数 + wchar_t *par_comment) // コメント +{ + unsigned char *header_buf = NULL; + wchar_t recovery_base[MAX_LEN]; + int err = 0, i; + int int4, buf_size; + unsigned int rv; + HANDLE *par_hFile = NULL; + file_ctx *files = NULL; + PHMD5 *par_md5 = NULL; + + buf_size = 0x0060 + (0x0038 * file_num) + (list_len * 2); // 最大サイズ分確保しておく + header_buf = malloc(buf_size); + if (header_buf == NULL){ + printf("malloc, %d\n", buf_size); + err = 1; + goto error_end; + } + + files = malloc(sizeof(file_ctx) * source_num); + if (files == NULL){ + printf("malloc, %d\n", sizeof(file_ctx) * source_num); + err = 1; + goto error_end; + } + for (i = 0; i < source_num; i++) + files[i].hFile = NULL; + + int4 = sizeof(HANDLE) * parity_num; + if (int4 == 0) + int4 = sizeof(HANDLE); + par_hFile = malloc(int4); + if (par_hFile == NULL){ + printf("malloc, %d\n", int4); + err = 1; + goto error_end; + } + par_hFile[0] = NULL; + for (i = 1; i < parity_num; i++) + par_hFile[i] = NULL; + + if (parity_num > 0){ + par_md5 = malloc(sizeof(PHMD5) * parity_num); + if (par_md5 == NULL){ + printf("malloc, %d\n", sizeof(PHMD5) * parity_num); + err = 1; + goto error_end; + } + for (i = 0; i < parity_num; i++) + Phmd5Begin(&(par_md5[i])); + } + + printf("\n"); + print_progress_text(0, "Computing file hash"); + + // ヘッダーを作成する + buf_size = set_header(header_buf, files, source_num, file_num, list_buf, list_len); + if (buf_size <= 0){ + if (buf_size < 0){ + err = 2; + } else { + err = 1; + } + goto error_end; + } + + if (switch_p == 0){ // インデックス・ファイルを作るなら + // コメントがあるならヘッダーを修正する + if (int4 = wcslen(par_comment) * 2) + memcpy(header_buf + 88, &int4, 4); + + // リカバリ・ファイル (*.PAR) を書き込む + par_hFile[0] = CreateFile(recovery_file, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (par_hFile[0] == INVALID_HANDLE_VALUE){ + print_win32_err(); + printf_cp("cannot create file, %s\n", recovery_file); + par_hFile[0] = NULL; + err = 1; + goto error_end; + } + if (!WriteFile(par_hFile[0], header_buf, buf_size, &rv, NULL)){ + print_win32_err(); + printf("WriteFile, index file\n"); + err = 1; + goto error_end; + } + // コメントがあるならコメントも書き込む + if (int4 = wcslen(par_comment) * 2){ + if (!WriteFile(par_hFile[0], par_comment, int4, &int4, NULL)){ + print_win32_err(); + printf("WriteFile, index file\n"); + err = 1; + goto error_end; + } + // MD5を計算しなおす + if (err = file_md5_from32(NULL, par_hFile[0], header_buf + 16)){ + if (err == 1) + printf("file_md5_from32\n"); + goto error_end; + } + if (SetFilePointer(par_hFile[0], 16, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER){ + print_win32_err(); + err = 1; + goto error_end; + } + if (!WriteFile(par_hFile[0], header_buf + 16, 16, &rv, NULL)){ + print_win32_err(); + printf("WriteFile, index file\n"); + err = 1; + goto error_end; + } + } + CloseHandle(par_hFile[0]); + par_hFile[0] = NULL; + } + print_progress_done(); + if (parity_num == 0) // パリティ・ブロックを作らない場合はここで終わる + goto creation_end; + + // ソース・ファイルから読み込む準備をする + for (i = 0; i < source_num; i++){ + if (SetFilePointer(files[i].hFile, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER){ + print_win32_err(); + err = 1; + goto error_end; + } + } + + // リカバリ・ファイル (*.PXX) の名前の基 + wcscpy(recovery_base, recovery_file); + int4 = wcslen(recovery_base); + recovery_base[int4 - 1] = 0; + recovery_base[int4 - 2] = 0; + print_progress_text(0, "Creating parity volume"); + + // リカバリ・ファイル (*.PXX) に書き込む準備をする + memcpy(header_buf + 88, &block_size, 8); // ブロック・サイズにする + for (i = 0; i < parity_num; i++){ + int4 = first_vol + i; // 作成する parity volume の番号にする + swprintf(recovery_file, MAX_LEN, L"%s%02d", recovery_base, int4); + par_hFile[i] = CreateFile(recovery_file, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (par_hFile[i] == INVALID_HANDLE_VALUE){ + print_win32_err(); + par_hFile[i] = NULL; + printf_cp("cannot create file, %s\n", recovery_file); + + // これまでに作ったリカバリ・ファイルを削除する + if (switch_p == 0){ + wcscpy(recovery_file, recovery_base); + wcscat(recovery_file, L"ar"); + DeleteFile(recovery_file); + } + for (int4 = 0; int4 < i; int4++){ + CloseHandle(par_hFile[int4]); + par_hFile[int4] = NULL; + swprintf(recovery_file, MAX_LEN, L"%s%02d", recovery_base, first_vol + int4); + DeleteFile(recovery_file); // 途中までのリカバリ・ファイルを削除する + } + + err = 1; + goto error_end; + } + + // ヘッダーを書き込む + memcpy(header_buf + 48, &int4, 2); // volume number にする + if (!WriteFile(par_hFile[i], header_buf, buf_size, &rv, NULL)){ + print_win32_err(); + printf("WriteFile, parity volume %d\n", int4); + err = 1; + goto error_end; + } + // ヘッダー部分からのハッシュ値を計算しかけておく + Phmd5Process(&(par_md5[i]), header_buf + 32, buf_size - 32); + } + + // パリティ・ブロックを作成する + if (err = rs_encode(source_num, block_size, parity_num, first_vol - 1, files, par_hFile, par_md5)){ + if (err == 2){ // キャンセルされたのなら、作業中のファイルを削除する + if (switch_p == 0){ + wcscpy(recovery_file, recovery_base); + wcscat(recovery_file, L"ar"); + DeleteFile(recovery_file); + } + for (i = 0; i < parity_num; i++){ + CloseHandle(par_hFile[i]); + par_hFile[i] = NULL; + swprintf(recovery_file, MAX_LEN, L"%s%02d", recovery_base, first_vol + i); + DeleteFile(recovery_file); // 途中までのリカバリ・ファイルを削除する + } + } + goto error_end; + } + + // リカバリ・ファイル (*.PXX) の MD5を計算しなおす + for (i = 0; i < parity_num; i++){ + Phmd5End(&(par_md5[i])); // ハッシュ値の計算終了 + memcpy(header_buf + 16, par_md5[i].hash, 16); + if (SetFilePointer(par_hFile[i], 16, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER){ + print_win32_err(); + err = 1; + goto error_end; + } + if (!WriteFile(par_hFile[i], header_buf + 16, 16, &rv, NULL)){ + print_win32_err(); + printf("WriteFile, parity volume %d\n", i); + err = 1; + goto error_end; + } + } + +creation_end: + printf("\nCreated successfully\n"); +error_end: + if (list_buf) + free(list_buf); + if (header_buf) + free(header_buf); + if (par_md5) + free(par_md5); + if (files){ + for (i = 0; i < source_num; i++){ + if (files[i].hFile != NULL) + CloseHandle(files[i].hFile); + } + free(files); + } + if (par_hFile){ + for (i = 0; i < parity_num; i++){ + if (par_hFile[i] != NULL) + CloseHandle(par_hFile[i]); + } + free(par_hFile); + } + return err; +} + +// ソース・ファイルの破損や欠損を調べる +int par1_verify( + int switch_b, // 既存のファイルを別名にしてどかす + int switch_p, // インデックス・ファイルを作り直す + wchar_t *par_comment) // コメント +{ + char ascii_buf[MAX_LEN * 3], ascii_buf2[MAX_LEN * 3]; + unsigned char *header_buf = NULL, *header_buf2 = NULL, hash[16], set_hash[16]; + wchar_t file_name[MAX_LEN], file_path[MAX_LEN], find_path[MAX_LEN]; + wchar_t par_char[] = L"AR"; + int err = 0, i, j, bad_flag, find_flag, parity_flag, par_flag, exist[99]; + int len, dir_len, base_len, buf_size, entry_size, entry_size2, blk; + int volume_num, volume_max, file_num, list_off, list_size, data_off; + int buf_size2, volume_num2, file_num2, list_off2, data_off2; + int parity_num, source_num, block_lost = 0, need_repair = 0, recovery_lost = 0; + unsigned int rv, meta_data[7]; + __int64 file_size, file_size2, data_size, block_size = 0, total_file_size; + HANDLE hFile = NULL, hFind; + WIN32_FIND_DATA FindData; + + // リカバリ・ファイルの名前 + wcscpy(file_path, recovery_file); + len = wcslen(file_path); + file_path[len - 2] = 0; // 末尾の2文字を消去する + if (file_path[len - 3] == 'p'){ + par_char[0] = 'a'; + par_char[1] = 'r'; + } + // ディレクトリの長さ + dir_len = len - 3; + while (dir_len >= 0){ + if ((file_path[dir_len] == '\\') || (file_path[dir_len] == '/')) + break; + dir_len--; + } + dir_len++; + base_len = wcslen(base_dir); + + // リカバリ・ファイルの一覧を表示する + printf("PAR File list :\n"); + printf(" Size : Filename\n"); + total_file_size = 0; + parity_num = 0; + volume_max = 0; + for (i = 0; i <= 99; i++){ + if (i == 0){ + swprintf(find_path, MAX_LEN, L"%s%s", file_path, par_char); + blk = 0; + } else { + swprintf(find_path, MAX_LEN, L"%s%02d", file_path, i); + blk = 1; + } + // ファイルを開くことができるか、サイズを取得できるかを確かめる + hFile = CreateFile(find_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + continue; + } + // ファイルのサイズを取得する + if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){ + CloseHandle(hFile); + continue; + } + CloseHandle(hFile); + total_file_size += file_size; + parity_num++; + volume_max = i; + // リカバリ・ファイルの名前 + utf16_to_cp(find_path + dir_len, ascii_buf); + printf("%13I64d : \"%s\"\n", file_size, ascii_buf); + } + hFile = NULL; + printf("\nPAR File total size\t: %I64d\n", total_file_size); + printf("PAR File possible count\t: %d\n\n", parity_num); + + // 指定されたリカバリ・ファイルを開く + hFile = CreateFile(recovery_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + printf("valid file is not found\n"); + err = 1; + goto error_end; + } + // ファイルのサイズを取得する + if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){ + print_win32_err(); + err = 1; + goto error_end; + } + + // ヘッダー用のバッファーを確保する + buf_size = 0x0060 + ((0x0038 + (260 * 2)) * 256) + (COMMENT_LEN * 2); // 最大サイズ + if ((__int64)buf_size > file_size) + buf_size = (unsigned int)file_size; + header_buf = malloc(buf_size); + if (header_buf == NULL){ + printf("malloc, %d\n", buf_size); + err = 1; + goto error_end; + } + + // PAR や PXX ファイルとして正しいかを調べる + par_flag = 0; + par_comment[0] = 0; + for (i = 0; i < 99; i++) + exist[i] = -1; + if (rv = get_header(NULL, hFile, header_buf, buf_size, set_hash, + &volume_num, &file_num, &list_off, &list_size, &data_off, &data_size)){ + if (rv == 2){ // キャンセル + err = 2; + goto error_end; + } + volume_num = -1; + par_flag |= 2; + } else { + // 各ブロックが存在するかを記録していく + j = get_source_num(header_buf, &block_size); + check_ini_file(set_hash, j); // 前回の検査結果が存在するかどうか + if (volume_num == 0){ + par_flag |= 1; + rv = (int)data_size; + if (buf_size < (int)data_size) + rv = buf_size; + if (rv > 1) + read_comment(par_comment, header_buf + data_off, rv); + } + } + CloseHandle(hFile); + hFile = NULL; + + // 他のリカバリ・ファイルのヘッダー用のバッファーを確保する + if (volume_num == -1){ + buf_size2 = 0x0060 + ((0x0038 + (260 * 2)) * 256) + (COMMENT_LEN * 2); // 最大サイズ + } else { + buf_size2 = 0x0060 + ((0x0038 + (260 * 2)) * file_num); + } + header_buf2 = malloc(buf_size2); + if (header_buf2 == NULL){ + printf("malloc, %d\n", buf_size2); + err = 1; + goto error_end; + } + + // 他のリカバリ・ファイルも調べる + printf("Loading PAR File:\n"); + printf(" Block Status : Filename\n"); + fflush(stdout); + parity_num = 0; + for (i = 0; i <= 99; i++){ + if (err = cancel_progress()) // キャンセル処理 + goto error_end; + if (i == 0){ + swprintf(find_path, MAX_LEN, L"%s%s", file_path, par_char); + blk = 0; + } else { + swprintf(find_path, MAX_LEN, L"%s%02d", file_path, i); + blk = 1; + } + utf16_to_cp(find_path + dir_len, ascii_buf); + + // 既にチェック済みなら + if ((_wcsicmp(recovery_file + dir_len, find_path + dir_len) == 0) && (volume_num != -1)){ + if ((volume_num > 0) && (volume_num <= 99)){ + if (exist[volume_num - 1] == -1) // 係数が重複してるのは使えない + parity_num++; + exist[volume_num - 1] = i; + if (volume_max < volume_num) + volume_max = volume_num; + } + if ((par_flag & 2) != 0){ + printf(" 0 / %d Damaged : \"%s\"\n", blk, ascii_buf); + recovery_lost++; + } else if (i == volume_num){ + printf(" %d / %d Good : \"%s\"\n", blk, blk, ascii_buf); + } else { + blk = (volume_num > 0) ? 1 : 0; + printf(" %d / %d Misnamed : \"%s\"\n", blk, blk, ascii_buf); + recovery_lost++; // リカバリ・ファイルの破損は修復必要とは判定しない + } + continue; + } + + // ファイルを開く + hFile = CreateFile(find_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + continue; + // PAR や PXX ファイルとして正しいかを調べる + bad_flag = 0; + if (volume_num == -1){ + if (rv = get_header(find_path + dir_len, hFile, header_buf, buf_size, set_hash, + &volume_num, &file_num, &list_off, &list_size, &data_off, &data_size)){ + if (rv == 2){ // キャンセル + err = 2; + goto error_end; + } + volume_num = -1; + bad_flag = 1; + } else { + j = get_source_num(header_buf, &block_size); + check_ini_file(set_hash, j); // 前回の検査結果が存在するかどうか + if (volume_num == 0){ + par_flag |= 1; + rv = (int)data_size; + if (buf_size < (int)data_size) + rv = buf_size; + if (rv > 1) + read_comment(par_comment, header_buf + data_off, (int)data_size); + } else if (volume_num <= 99){ + parity_num++; + exist[volume_num - 1] = i; + if (volume_max < volume_num) + volume_max = volume_num; + } + if (i != volume_num){ // ブロック番号と拡張子が異なるなら + bad_flag = 3; + blk = (volume_num > 0) ? 1 : 0; + } + } + } else { + if (rv = get_header(find_path + dir_len, hFile, header_buf2, buf_size2, hash, + &volume_num2, &file_num2, &list_off2, NULL, &data_off2, &data_size)){ + if (rv == 2){ // キャンセル + err = 2; + goto error_end; + } + bad_flag = 1; + } else { // 同じ構成の PXX かを確認する + if (memcmp(hash, set_hash, 16) != 0) + bad_flag = 2; + if ((file_num2 != file_num) || (list_off2 != list_off) || (data_off2 != data_off)) + bad_flag = 2; + if ((block_size) && (volume_num2 > 0)){ + if (data_size != block_size) + bad_flag = 2; + } + if (!bad_flag){ + if (volume_num2 == 0){ + par_flag |= 1; + rv = (int)data_size; + if (buf_size2 < (int)data_size) + rv = buf_size2; + if (rv > 1) + read_comment(par_comment, header_buf2 + data_off2, rv); + } else if (volume_num2 <= 99){ + if (exist[volume_num2 - 1] == -1) // 係数が重複してるのは使えない + parity_num++; + exist[volume_num2 - 1] = i; + if (volume_max < volume_num2) + volume_max = volume_num2; + } + if (i != volume_num2){ // ブロック番号と拡張子が異なるなら + bad_flag = 3; + blk = (volume_num2 > 0) ? 1 : 0; + } + } + } + } + switch (bad_flag){ + case 0: + printf(" %d / %d Good : \"%s\"\n", blk, blk, ascii_buf); + break; + case 1: + printf(" 0 / %d Damaged : \"%s\"\n", blk, ascii_buf); + recovery_lost++; + break; + case 2: + printf(" %d / %d Useless : \"%s\"\n", blk, blk, ascii_buf); + recovery_lost++; + break; + case 3: + printf(" %d / %d Misnamed : \"%s\"\n", blk, blk, ascii_buf); + recovery_lost++; // リカバリ・ファイルの破損は修復必要とは判定しない + break; + } + CloseHandle(hFile); + hFile = NULL; + + fflush(stdout); + } + + if (volume_num == -1){ // Recovery Set が見つからなければここで終わる + printf("valid file is not found\n"); + err = 1; + goto error_end; + } + // セット・ハッシュとプログラムのバージョンを表示する + printf("\nSet Hash: "); + print_hash(set_hash); + memcpy(&rv, header_buf + 12, 4); + printf("\nCreator : "); + switch (rv >> 24){ + case 1: // Mirror + printf("Mirror"); + break; + case 2: // PAR + printf("PAR"); + break; + case 3: // SmartPar + printf("SmartPar"); + break; + default: + printf("Unknown client %d", rv >> 24); + } + printf(" version %d.%d.%d\n", (rv >> 16) & 0xFF, (rv >> 8) & 0xFF, rv & 0xFF); + if ((par_comment[0] != 0) && (!utf16_to_cp(par_comment, ascii_buf))) + printf("Comment : %s\n", ascii_buf); // コメントをユニコードから戻せた場合だけ表示する + + if (((par_flag & 1) == 0) && (switch_p)){ // PAR ファイルを作り直す + swprintf(find_path, MAX_LEN, L"%s%s", file_path, par_char); + get_temp_name(find_path, file_name); + if (recreate_par(file_name, header_buf, header_buf2) == 0){ + if (replace_file(find_path, file_name, switch_b) == 0){ // 破損したPARファイルが存在するならどかす + utf16_to_cp(find_path + dir_len, ascii_buf); + printf("\nRestored file :\n"); + printf(" Size : Filename\n"); + printf("%13u : \"%s\"\n", data_off, ascii_buf); + } else { + DeleteFile(file_name); + } + } + } + free(header_buf2); + header_buf2 = NULL; + printf("\nParity Volume count\t: %d\n", volume_max); + printf("Parity Volume found\t: %d\n", parity_num); + fflush(stdout); + + // ファイル・リストを表示する + printf("\nInput File list : %d\n", file_num); + printf(" Size B : Filename\n"); + total_file_size = 0; + source_num = 0; + list_off2 = list_off; + for (i = 0; i < file_num; i++){ + // entry size + memcpy(&entry_size, header_buf + list_off2, 4); + // status field + memcpy(&parity_flag, header_buf + (list_off2 + 8), 2); + parity_flag &= 0x01; // bit 0 = file is saved in the parity volume set + if (parity_flag){ + source_num++; + blk = 1; + } else { + blk = 0; + } + // size + memcpy(&data_size, header_buf + (list_off2 + 16), 8); + total_file_size += data_size; + // filename + len = (entry_size - 56) / 2; + if (len >= MAX_LEN) + len = MAX_LEN - 1; + memcpy(file_name, header_buf + (list_off2 + 56), len * 2); + file_name[len] = 0; + utf16_to_cp(file_name, ascii_buf); + // ファイル名が有効かどうか確かめる + j = sanitize_filename(file_name); + if (j != 0){ + if (j == 16){ + printf("filename is not valied, %s\n", ascii_buf); + err = 1; + goto error_end; + } + utf16_to_cp(file_name, ascii_buf); + } + printf("%13I64u %d : \"%s\"\n", data_size, blk, ascii_buf); + list_off2 += entry_size; // 次のエントリー位置にずらす + } + printf("\nData File count : %d\n", source_num); + printf("Max file size\t: %I64d\n", block_size); + printf("Total data size : %I64d\n", total_file_size); + + // ソース・ファイルが存在していて正しいかを調べる + printf("\nVerifying Input File :\n"); + printf(" Size Status : Filename\n"); + fflush(stdout); + for (i = 0; i < file_num; i++){ + if (err = cancel_progress()) // キャンセル処理 + goto error_end; + find_flag = bad_flag = parity_flag = 0; + + // entry size + memcpy(&entry_size, header_buf + list_off, 4); + // status field + memcpy(&parity_flag, header_buf + (list_off + 8), 2); + parity_flag &= 0x01; // bit 0 = file is saved in the parity volume set + // size + memcpy(&data_size, header_buf + (list_off + 16), 8); + file_size = 0; + // filename + len = (entry_size - 56) / 2; + if (len >= MAX_LEN) + len = MAX_LEN - 1; + memcpy(file_name, header_buf + (list_off + 56), len * 2); + file_name[len] = 0; + utf16_to_cp(file_name, ascii_buf); + // ファイル名が有効かどうか確かめる + wcscpy(file_path, file_name); + j = sanitize_filename(file_path); + if (j != 0){ + wcscpy(file_name, file_path); + utf16_to_cp(file_name, ascii_buf); + } + // ファイル名を基準ディレクトリに追加してパスにする + if (base_len + wcslen(file_name) >= MAX_LEN - ADD_LEN){ + printf("filename is too long\n"); + err = 1; + goto error_end; + } + wcscpy(file_path, base_dir); + wcscpy(file_path + base_len, file_name); + + // ファイルが存在するか + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile != INVALID_HANDLE_VALUE){ // ファイルが存在するなら内容を確認する + // 検査結果の記録があるかどうか + bad_flag = check_ini_state(i, meta_data, hFile); + memcpy(&file_size, meta_data, 8); + if (bad_flag == -1){ // ファイル・サイズが不明なら消失扱いにする + bad_flag = 1; + } else if (bad_flag == -2){ // 検査結果が無かった場合 + bad_flag = 0; + if (file_size >= data_size){ // 末尾にゴミが付いてないか調べる + if (file_size > data_size) + bad_flag = 2; + if (data_size > 0){ // 16k MD5 hash が一致するか確かめる + if (file_md5_16k(hFile, data_size, hash)){ + bad_flag = 1; + } else if (memcmp(hash, header_buf + (list_off + 40), 16) != 0){ + bad_flag = 1; + } else if (data_size > 16384){ // MD5 hash が一致するか確かめる + if (rv = file_md5(file_name, hFile, data_size, hash)){ + if (rv == 2){ + err = 2; + goto error_end; + } + bad_flag = 1; + } else if (memcmp(hash, header_buf + (list_off + 24), 16) != 0){ + bad_flag = 1; + } + // 16k MD5 hash が一致した時だけ検査結果を記録する + write_ini_state(i, meta_data, bad_flag); + } + } + } else if (file_size < data_size){ + bad_flag = 1; + } + } + CloseHandle(hFile); + hFile = NULL; + if (!bad_flag) + find_flag = 3; + } + + // ファイルが存在しないか、不完全だった場合は、サイズとハッシュ値で検索する + if ((data_size) && ((hFile == INVALID_HANDLE_VALUE) || (bad_flag))){ + wcscpy(find_path, base_dir); + wcscpy(find_path + base_len, L"*"); + hFind = FindFirstFile(find_path, &FindData); + if (hFind != INVALID_HANDLE_VALUE){ + do { + if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0){ // フォルダは無視する + find_flag = 1; + file_size2 = ((__int64)(FindData.nFileSizeHigh) << 32) | (__int64)(FindData.nFileSizeLow); + if (file_size2 != data_size){ // ファイル・サイズが一致しなければ + find_flag = 0; + } else { // ファイル名がリスト上にあれば除外する + memcpy(&list_off2, header_buf + 64, 4); + for (j = 0; j < file_num; j++){ + // entry size + memcpy(&entry_size2, header_buf + list_off2, 4); + // filename + len = (entry_size2 - 56) / 2; + if (len >= MAX_LEN) + len = MAX_LEN - 1; + memcpy(file_name, header_buf + (list_off2 + 56), len * 2); + file_name[len] = 0; + sanitize_filename(file_name); + if (_wcsicmp(file_name, FindData.cFileName) == 0){ + find_flag = 0; + break; + } + list_off2 += entry_size2; // 次のエントリー位置にずらす + } + } + // リスト上に無くてファイル・サイズが一致すれば + if ((find_flag != 0) && (base_len + wcslen(FindData.cFileName) < MAX_LEN)){ + wcscpy(find_path + base_len, FindData.cFileName); + hFile = CreateFile(find_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile != INVALID_HANDLE_VALUE){ + j = check_ini_state(i, meta_data, hFile); + if (j == -2){ // 検査結果が無かった場合 + if (data_size > 0){ // 16k MD5 hash が一致するか確かめる + if (file_md5_16k(hFile, data_size, hash)){ + find_flag = 0; + } else if (memcmp(hash, header_buf + (list_off + 40), 16) != 0){ + find_flag = 0; + } else if (data_size > 16384){ // MD5 hash が一致するか確かめる + j = 0; + if (rv = file_md5(FindData.cFileName, hFile, data_size, hash)){ + if (rv == 2){ + err = 2; + goto error_end; + } + find_flag = 0; + j = 1; + } else if (memcmp(hash, header_buf + (list_off + 24), 16) != 0){ + find_flag = 0; + j = 1; + } + // 16k MD5 hash が一致した時だけ検査結果を記録する + write_ini_state(i, meta_data, j); + } + } + } else if (j != 0){ // 完全以外なら + find_flag = 0; + } + CloseHandle(hFile); + hFile = NULL; + if (find_flag){ // 異なる名前だが同じ内容のファイルを発見した + utf16_to_cp(FindData.cFileName, ascii_buf2); + break; + } + } else { + find_flag = 0; + } + } + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + } + } + if (find_flag == 3){ + printf(" = Complete : \"%s\"\n", ascii_buf); + } else if (find_flag == 1){ + need_repair++; + if (bad_flag){ + printf("%13I64u Damaged~ : \"%s\"\n", file_size, ascii_buf); + } else { + printf(" - Missing~ : \"%s\"\n", ascii_buf); + } + printf("%13I64u Misnamed : \"%s\"\n", data_size, ascii_buf2); + } else if (bad_flag == 2){ + need_repair++; + printf("%13I64u Appended : \"%s\"\n", file_size, ascii_buf); + } else if (bad_flag){ + if (parity_flag) + block_lost++; + if (data_size == 0) + need_repair++; + printf("%13I64u Damaged : \"%s\"\n", file_size, ascii_buf); + } else { + if (parity_flag) + block_lost++; + if (data_size == 0) + need_repair++; + printf(" - Missing : \"%s\"\n", ascii_buf); + } + + fflush(stdout); + list_off += entry_size; // 次のエントリー位置にずらす + } + printf("\nData File lost\t: %d\n\n", block_lost); + if ((block_lost == 0) && (need_repair == 0)) + printf("All Files Complete\n"); + if (recovery_lost > 0){ // 不完全なリカバリ・ファイルがあるなら + err = 256; + printf("%d PAR File(s) Incomplete\n", recovery_lost); + } + + // 修復する必要があるかどうか + if ((block_lost > 0) || (need_repair > 0)){ + err |= 4; + if (need_repair > 0){ + printf("Ready to rename %d file(s)\n", need_repair); + err |= 32; + } + if (block_lost > 0){ + if (block_lost > parity_num){ + printf("Need %d more volume(s) to repair %d file(s)\n", block_lost - parity_num, block_lost); + err |= 8; + } else { + printf("Ready to repair %d file(s)\n", block_lost); + err |= 128; + } + } + } + +error_end: + close_ini_file(); + if (hFile != NULL) + CloseHandle(hFile); + if (header_buf) + free(header_buf); + if (header_buf2) + free(header_buf2); + return err; +} + +// ソース・ファイルの破損や欠損を修復する +int par1_repair( + int switch_b, // 既存のファイルを別名にしてどかす + int switch_p, // インデックス・ファイルを作り直す + wchar_t *par_comment) // コメント +{ + char ascii_buf[MAX_LEN * 3], ascii_buf2[MAX_LEN * 3]; + unsigned char *header_buf = NULL, *header_buf2 = NULL, hash[16], set_hash[16]; + wchar_t file_name[MAX_LEN * 2], file_path[MAX_LEN], find_path[MAX_LEN * 2]; + wchar_t par_char[] = L"AR"; + int err = 0, i, j, bad_flag, find_flag, parity_flag, par_flag, exist[256]; + int len, dir_len, base_len, buf_size, entry_size, entry_size2, blk; + int volume_num, volume_max, file_num, list_off, list_size, data_off; + int buf_size2, volume_num2, file_num2, list_off2, data_off2; + int parity_num, source_num, block_lost = 0, need_repair = 0, recovery_lost = 0; + int parity_max, block_max; + unsigned int rv, meta_data[7]; + __int64 file_size, file_size2, data_size, block_size = 0, total_file_size; + HANDLE hFile = NULL, hFind; + WIN32_FIND_DATA FindData; + file_ctx *files = NULL, *tmp_p; + + // リカバリ・ファイルの名前 + wcscpy(file_path, recovery_file); + len = wcslen(file_path); + file_path[len - 2] = 0; // 末尾の2文字を消去する + if (file_path[len - 3] == 'p'){ + par_char[0] = 'a'; + par_char[1] = 'r'; + } + // ディレクトリの長さ + dir_len = len - 3; + while (dir_len >= 0){ + if ((file_path[dir_len] == '\\') || (file_path[dir_len] == '/')) + break; + dir_len--; + } + dir_len++; + base_len = wcslen(base_dir); + + // リカバリ・ファイルの一覧を表示する + printf("PAR File list :\n"); + printf(" Size : Filename\n"); + total_file_size = 0; + parity_num = 0; + volume_max = 0; + for (i = 0; i <= 99; i++){ + if (i == 0){ + swprintf(find_path, MAX_LEN, L"%s%s", file_path, par_char); + blk = 0; + } else { + swprintf(find_path, MAX_LEN, L"%s%02d", file_path, i); + blk = 1; + } + // ファイルを開くことができるか、サイズを取得できるかを確かめる + hFile = CreateFile(find_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + continue; + } + // ファイルのサイズを取得する + if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){ + CloseHandle(hFile); + continue; + } + CloseHandle(hFile); + total_file_size += file_size; + parity_num++; + volume_max = i; + // リカバリ・ファイルの名前 + utf16_to_cp(find_path + dir_len, ascii_buf); + printf("%13I64d : \"%s\"\n", file_size, ascii_buf); + } + hFile = NULL; + printf("\nPAR File total size\t: %I64d\n", total_file_size); + printf("PAR File possible count\t: %d\n\n", parity_num); + + // 指定されたリカバリ・ファイルを開く + hFile = CreateFile(recovery_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + printf("valid file is not found\n"); + err = 1; + goto error_end; + } + // ファイルのサイズを取得する + if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){ + print_win32_err(); + err = 1; + goto error_end; + } + + // ヘッダー用のバッファーを確保する + buf_size = 0x0060 + ((0x0038 + (260 * 2)) * 256) + (COMMENT_LEN * 2); // 最大サイズ + if ((__int64)buf_size > file_size) + buf_size = (unsigned int)file_size; + header_buf = malloc(buf_size); + if (header_buf == NULL){ + printf("malloc, %d\n", buf_size); + err = 1; + goto error_end; + } + + // PAR や PXX ファイルとして正しいかを調べる + parity_max = 0; + par_flag = 0; + par_comment[0] = 0; + for (i = 0; i < 256; i++) + exist[i] = -1; + if (rv = get_header(NULL, hFile, header_buf, buf_size, set_hash, + &volume_num, &file_num, &list_off, &list_size, &data_off, &data_size)){ + if (rv == 2){ // キャンセル + err = 2; + goto error_end; + } + volume_num = -1; + par_flag |= 2; + } else { + // 各ブロックが存在するかを記録していく + block_max = get_source_num(header_buf, &block_size); + check_ini_file(set_hash, block_max); // 前回の検査結果が存在するかどうか + if (volume_num == 0){ + par_flag |= 1; + rv = (int)data_size; + if (buf_size < (int)data_size) + rv = buf_size; + if (rv > 1) + read_comment(par_comment, header_buf + data_off, rv); + } + } + CloseHandle(hFile); + hFile = NULL; + + // 他のリカバリ・ファイルのヘッダー用のバッファーを確保する + if (volume_num == -1){ + buf_size2 = 0x0060 + ((0x0038 + (260 * 2)) * 256) + (COMMENT_LEN * 2); // 最大サイズ + } else { + buf_size2 = 0x0060 + ((0x0038 + (260 * 2)) * file_num); + } + header_buf2 = malloc(buf_size2); + if (header_buf2 == NULL){ + printf("malloc, %d\n", buf_size2); + err = 1; + goto error_end; + } + + // 他のリカバリ・ファイルも調べる + printf("Loading PAR File:\n"); + printf(" Block Status : Filename\n"); + fflush(stdout); + parity_num = 0; + for (i = 0; i <= 99; i++){ + if (err = cancel_progress()) // キャンセル処理 + goto error_end; + if (i == 0){ + swprintf(find_path, MAX_LEN, L"%s%s", file_path, par_char); + blk = 0; + } else { + swprintf(find_path, MAX_LEN, L"%s%02d", file_path, i); + blk = 1; + } + utf16_to_cp(find_path + dir_len, ascii_buf); + + // 既にチェック済みなら + if ((_wcsicmp(recovery_file + dir_len, find_path + dir_len) == 0) && (volume_num != -1)){ + if ((volume_num > 0) && (volume_num <= 99)){ + if (exist[block_max + volume_num - 1] == -1) // 係数が重複してるのは使えない + parity_num++; + if (parity_max < volume_num) + parity_max = volume_num; + if (block_max + volume_num <= 256) + exist[block_max + volume_num - 1] = i; + if (volume_max < volume_num) + volume_max = volume_num; + } + if ((par_flag & 2) != 0){ + printf(" 0 / %d Damaged : \"%s\"\n", blk, ascii_buf); + recovery_lost++; + } else if (i == volume_num){ + printf(" %d / %d Good : \"%s\"\n", blk, blk, ascii_buf); + } else { + blk = (volume_num > 0) ? 1 : 0; + printf(" %d / %d Misnamed : \"%s\"\n", blk, blk, ascii_buf); + recovery_lost++; // リカバリ・ファイルの破損は修復必要とは判定しない + } + continue; + } + + // ファイルを開く + hFile = CreateFile(find_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + continue; + // PAR や PXX ファイルとして正しいかを調べる + bad_flag = 0; + if (volume_num == -1){ + if (rv = get_header(find_path + dir_len, hFile, header_buf, buf_size, set_hash, + &volume_num, &file_num, &list_off, &list_size, &data_off, &data_size)){ + if (rv == 2){ // キャンセル + err = 2; + goto error_end; + } + volume_num = -1; + bad_flag = 1; + } else { + block_max = get_source_num(header_buf, &block_size); + check_ini_file(set_hash, block_max); // 前回の検査結果が存在するかどうか + if (volume_num == 0){ + par_flag |= 1; + rv = (int)data_size; + if (buf_size < (int)data_size) + rv = buf_size; + if (rv > 1) + read_comment(par_comment, header_buf + data_off, (int)data_size); + } else if (volume_num <= 99){ + parity_num++; + if (parity_max < volume_num) + parity_max = volume_num; + if (block_max + volume_num <= 256) + exist[block_max + volume_num - 1] = i; + if (volume_max < volume_num) + volume_max = volume_num; + } + if (i != volume_num){ // ブロック番号と拡張子が異なるなら + bad_flag = 3; + blk = (volume_num > 0) ? 1 : 0; + } + } + } else { + if (rv = get_header(find_path + dir_len, hFile, header_buf2, buf_size2, hash, + &volume_num2, &file_num2, &list_off2, NULL, &data_off2, &data_size)){ + if (rv == 2){ // キャンセル + err = 2; + goto error_end; + } + bad_flag = 1; + } else { // 同じ構成の PXX かを確認する + if (memcmp(hash, set_hash, 16) != 0) + bad_flag = 2; + if ((file_num2 != file_num) || (list_off2 != list_off) || (data_off2 != data_off)) + bad_flag = 2; + if ((block_size) && (volume_num2 > 0)){ + if (data_size != block_size) + bad_flag = 2; + } + if (!bad_flag){ + if (volume_num2 == 0){ + par_flag |= 1; + rv = (int)data_size; + if (buf_size2 < (int)data_size) + rv = buf_size2; + if (rv > 1) + read_comment(par_comment, header_buf2 + data_off2, rv); + } else if (volume_num2 <= 99){ + if (exist[block_max + volume_num2 - 1] == -1) // 係数が重複してるのは使えない + parity_num++; + // 各ブロックが存在するかを記録していく + if (parity_max < volume_num2) + parity_max = volume_num2; + if (block_max + volume_num2 <= 256) + exist[block_max + volume_num2 - 1] = i; + if (volume_max < volume_num2) + volume_max = volume_num2; + } + if (i != volume_num2){ // ブロック番号と拡張子が異なるなら + bad_flag = 3; + blk = (volume_num2 > 0) ? 1 : 0; + } + } + } + } + switch (bad_flag){ + case 0: + printf(" %d / %d Good : \"%s\"\n", blk, blk, ascii_buf); + break; + case 1: + printf(" 0 / %d Damaged : \"%s\"\n", blk, ascii_buf); + recovery_lost++; + break; + case 2: + printf(" %d / %d Useless : \"%s\"\n", blk, blk, ascii_buf); + recovery_lost++; + break; + case 3: + printf(" %d / %d Misnamed : \"%s\"\n", blk, blk, ascii_buf); + recovery_lost++; // リカバリ・ファイルの破損は修復必要とは判定しない + break; + } + CloseHandle(hFile); + hFile = NULL; + + fflush(stdout); + } + + if (volume_num == -1){ // Recovery Set が見つからなければここで終わる + printf("valid file is not found\n"); + err = 1; + goto error_end; + } + // セット・ハッシュとプログラムのバージョンを表示する + printf("\nSet Hash: "); + print_hash(set_hash); + memcpy(&rv, header_buf + 12, 4); + printf("\nCreator : "); + switch (rv >> 24){ + case 1: // Mirror + printf("Mirror"); + break; + case 2: // PAR + printf("PAR"); + break; + case 3: // SmartPar + printf("SmartPar"); + break; + default: + printf("Unknown client %d", rv >> 24); + } + printf(" version %d.%d.%d\n", (rv >> 16) & 0xFF, (rv >> 8) & 0xFF, rv & 0xFF); + if ((par_comment[0] != 0) && (!utf16_to_cp(par_comment, ascii_buf))) + printf("Comment : %s\n", ascii_buf); // コメントをユニコードから戻せた場合だけ表示する + + if (((par_flag & 1) == 0) && (switch_p)){ // PAR ファイルを作り直す + swprintf(find_path, MAX_LEN, L"%s%s", file_path, par_char); + get_temp_name(find_path, file_name); + if (recreate_par(file_name, header_buf, header_buf2) == 0){ + if (replace_file(find_path, file_name, switch_b) == 0){ // 破損したPARファイルが存在するならどかす + utf16_to_cp(find_path + dir_len, ascii_buf); + printf("\nRestored file :\n"); + printf(" Size : Filename\n"); + printf("%13u : \"%s\"\n", data_off, ascii_buf); + } else { + DeleteFile(file_name); + } + } + } + free(header_buf2); + header_buf2 = NULL; + printf("\nParity Volume count\t: %d\n", volume_max); + printf("Parity Volume found\t: %d\n", parity_num); + fflush(stdout); + + // リカバリ・ファイルを開く + len = sizeof(file_ctx) * (block_max + parity_max); + files = malloc(len); + if (files == NULL){ + printf("malloc, %d\n", len); + err = 1; + goto error_end; + } + for (i = 0; i < (block_max + parity_max); i++) + files[i].hFile = NULL; + for (i = block_max; i < (block_max + parity_max); i++){ + if (exist[i] != -1){ // リカバリ・ファイルが存在するなら + if (exist[i] == 0){ + swprintf(find_path, MAX_LEN, L"%s%s", file_path, par_char); + } else { + swprintf(find_path, MAX_LEN, L"%s%02d", file_path, exist[i]); + } + files[i].hFile = CreateFile(find_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (files[i].hFile == INVALID_HANDLE_VALUE){ + exist[i] = 0; + continue; + } + // ファイル・サイズを取得する + if (!GetFileSizeEx(files[i].hFile, (PLARGE_INTEGER)&file_size2)){ + exist[i] = 0; + continue; + } + files[i].size = file_size2; + files[i].size -= data_off; + //printf("%d: off = %d, size = %I64u \n", i, data_off, files[i].size); + // 開始位置をリード・ソロモン符号のデータの所にしておく + if (SetFilePointer(files[i].hFile, data_off, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER){ + exist[i] = 0; + continue; + } + exist[i] = 1; + } else { + exist[i] = 0; + } + } + for (i = 0; i < block_max; i++) + exist[i] = 0; + + // ファイル・リストを表示する + printf("\nInput File list : %d\n", file_num); + printf(" Size B : Filename\n"); + total_file_size = 0; + source_num = 0; + list_off2 = list_off; + for (i = 0; i < file_num; i++){ + // entry size + memcpy(&entry_size, header_buf + list_off2, 4); + // status field + memcpy(&parity_flag, header_buf + (list_off2 + 8), 2); + parity_flag &= 0x01; // bit 0 = file is saved in the parity volume set + if (parity_flag){ + source_num++; + blk = 1; + } else { + blk = 0; + } + // size + memcpy(&data_size, header_buf + (list_off2 + 16), 8); + total_file_size += data_size; + // filename + len = (entry_size - 56) / 2; + if (len >= MAX_LEN) + len = MAX_LEN - 1; + memcpy(file_name, header_buf + (list_off2 + 56), len * 2); + file_name[len] = 0; + utf16_to_cp(file_name, ascii_buf); + // ファイル名が有効かどうか確かめる + j = sanitize_filename(file_name); + if (j != 0){ + if (j == 16){ + printf("filename is not valied, %s\n", ascii_buf); + err = 1; + goto error_end; + } + utf16_to_cp(file_name, ascii_buf); + } + printf("%13I64u %d : \"%s\"\n", data_size, blk, ascii_buf); + list_off2 += entry_size; // 次のエントリー位置にずらす + } + printf("\nData File count : %d\n", source_num); + printf("Max file size\t: %I64d\n", block_size); + printf("Total data size : %I64d\n", total_file_size); + + // ソース・ファイルが存在していて正しいかを調べる + printf("\nVerifying Input File :\n"); + printf(" Size Status : Filename\n"); + fflush(stdout); + source_num = 0; + for (i = 0; i < file_num; i++){ + if (err = cancel_progress()) // キャンセル処理 + goto error_end; + find_flag = bad_flag = parity_flag = 0; + + // entry size + memcpy(&entry_size, header_buf + list_off, 4); + // status field + memcpy(&parity_flag, header_buf + (list_off + 8), 2); + parity_flag &= 0x0001; // bit 0 = file is saved in the parity volume set + if (parity_flag) + source_num++; + // size + memcpy(&data_size, header_buf + (list_off + 16), 8); + file_size = 0; + // filename + len = (entry_size - 56) / 2; + if (len >= MAX_LEN) + len = MAX_LEN - 1; + memcpy(file_name, header_buf + (list_off + 56), len * 2); + file_name[len] = 0; + utf16_to_cp(file_name, ascii_buf); + // ファイル名が有効かどうか確かめる + wcscpy(file_path, file_name); + j = sanitize_filename(file_path); + if (j != 0){ + wcscpy(file_name, file_path); + utf16_to_cp(file_name, ascii_buf); + } + // ファイル名を基準ディレクトリに追加してパスにする + if (base_len + wcslen(file_name) >= MAX_LEN - ADD_LEN){ + printf("filename is too long\n"); + err = 1; + goto error_end; + } + wcscpy(file_path, base_dir); + wcscpy(file_path + base_len, file_name); + + // ファイルが存在するか + hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile != INVALID_HANDLE_VALUE){ // ファイルが存在するなら内容を確認する + // 検査結果の記録があるかどうか + bad_flag = check_ini_state(i, meta_data, hFile); + memcpy(&file_size, meta_data, 8); + if (bad_flag == -1){ // ファイル・サイズが不明なら消失扱いにする + bad_flag = 1; + } else if (bad_flag == -2){ // 検査結果が無かった場合 + bad_flag = 0; + if (file_size >= data_size){ // 末尾にゴミが付いてないか調べる + if (file_size > data_size) + bad_flag = 2; + if (data_size > 0){ // 16k MD5 hash が一致するか確かめる + if (file_md5_16k(hFile, data_size, hash)){ + bad_flag = 1; + } else if (memcmp(hash, header_buf + (list_off + 40), 16) != 0){ + bad_flag = 1; + } else if (data_size > 16384){ // MD5 hash が一致するか確かめる + if (rv = file_md5(file_name, hFile, data_size, hash)){ + if (rv == 2){ + err = 2; + goto error_end; + } + bad_flag = 1; + } else if (memcmp(hash, header_buf + (list_off + 24), 16) != 0){ + bad_flag = 1; + } + // 16k MD5 hash が一致した時だけ検査結果を記録する + write_ini_state(i, meta_data, bad_flag); + } + } + } else if (file_size < data_size){ + bad_flag = 1; + } + } + CloseHandle(hFile); + hFile = NULL; + if (!bad_flag){ + find_flag = 3; + if (parity_flag) + exist[source_num - 1] = 1; + } + } + + // ファイルが存在しないか、不完全だった場合は、サイズとハッシュ値で検索する + if ((data_size) && ((hFile == INVALID_HANDLE_VALUE) || (bad_flag))){ + wcscpy(find_path, base_dir); + wcscpy(find_path + base_len, L"*"); + hFind = FindFirstFile(find_path, &FindData); + if (hFind != INVALID_HANDLE_VALUE){ + do { + if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0){ // フォルダは無視する + find_flag = 1; + file_size2 = ((__int64)(FindData.nFileSizeHigh) << 32) | (__int64)(FindData.nFileSizeLow); + if (file_size2 != data_size){ // ファイル・サイズが一致しなければ + find_flag = 0; + } else { // ファイル名がリスト上にあれば除外する + memcpy(&list_off2, header_buf + 64, 4); + for (j = 0; j < file_num; j++){ + // entry size + memcpy(&entry_size2, header_buf + list_off2, 4); + // filename + len = (entry_size2 - 56) / 2; + if (len >= MAX_LEN) + len = MAX_LEN - 1; + memcpy(file_name, header_buf + (list_off2 + 56), len * 2); + file_name[len] = 0; + sanitize_filename(file_name); + if (_wcsicmp(file_name, FindData.cFileName) == 0){ + find_flag = 0; + break; + } + list_off2 += entry_size2; // 次のエントリー位置にずらす + } + } + // リスト上に無くてファイル・サイズが一致すれば + if ((find_flag != 0) && (base_len + wcslen(FindData.cFileName) < MAX_LEN)){ + wcscpy(find_path + base_len, FindData.cFileName); + hFile = CreateFile(find_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile != INVALID_HANDLE_VALUE){ + j = check_ini_state(i, meta_data, hFile); + if (j == -2){ // 検査結果が無かった場合 + if (data_size > 0){ // 16k MD5 hash が一致するか確かめる + if (file_md5_16k(hFile, data_size, hash)){ + find_flag = 0; + } else if (memcmp(hash, header_buf + (list_off + 40), 16) != 0){ + find_flag = 0; + } else if (data_size > 16384){ // MD5 hash が一致するか確かめる + j = 0; + if (rv = file_md5(FindData.cFileName, hFile, data_size, hash)){ + if (rv == 2){ + err = 2; + goto error_end; + } + find_flag = 0; + j = 1; + } else if (memcmp(hash, header_buf + (list_off + 24), 16) != 0){ + find_flag = 0; + j = 1; + } + // 16k MD5 hash が一致した時だけ検査結果を記録する + write_ini_state(i, meta_data, j); + } + } + } else if (j != 0){ // 完全以外なら + find_flag = 0; + } + CloseHandle(hFile); + hFile = NULL; + if (find_flag){ // 異なる名前だが同じ内容のファイルを発見した + utf16_to_cp(FindData.cFileName, ascii_buf2); + // ファイル名を修正する + if (replace_file(file_path, find_path, switch_b) == 0){ + find_flag = 2; +// } else { +// printf("cannot rename to %s\n", ascii_buf); +// err = 1; +// goto error_end; + } + break; + } + } else { + find_flag = 0; + } + } + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + } + } + if (find_flag == 3){ + printf(" = Complete : \"%s\"\n", ascii_buf); + } else if (find_flag == 1){ + need_repair++; + need_repair |= 0x40000000; // 修復失敗の印 + if (bad_flag){ + printf("%13I64u Failed~ : \"%s\"\n", file_size, ascii_buf); + } else { + printf(" - Failed : \"%s\"\n", ascii_buf); + } + printf("%13I64u Misnamed : \"%s\"\n", data_size, ascii_buf2); + } else if (find_flag == 2){ + need_repair += 0x10001; // 修復できた数を記録する + if (parity_flag) + exist[source_num - 1] = 1; + printf("%13I64u Restored : \"%s\"\n", data_size, ascii_buf); + } else if (bad_flag == 2){ + if (shorten_file(file_path, data_size, switch_b) != 0) // ファイルを小さくする + bad_flag = 1; + need_repair++; + if (bad_flag == 2){ // 修復 (縮小) できたら + need_repair += 0x10000; // 上位 16-bit に修復できた数を記録する + if (parity_flag) + exist[source_num - 1] = 1; + printf("%13I64u Restored : \"%s\"\n", data_size, ascii_buf); + } else { // 修復できなかったなら + if (parity_flag) + block_lost++; + printf("%13I64u Appended : \"%s\"\n", file_size, ascii_buf); + } + } else if (bad_flag){ + bad_flag = 1; + if (data_size == 0){ // サイズが 0ならすぐに復元できる + need_repair++; + hFile = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE){ + bad_flag = 0; + need_repair += 0x10000; // 上位 16-bit に修復できた数を記録する + } + CloseHandle(hFile); + hFile = NULL; + } + if (bad_flag){ + if (parity_flag) + block_lost++; + printf("%13I64u Damaged : \"%s\"\n", file_size, ascii_buf); + } else { + if (parity_flag) + exist[source_num - 1] = 1; + printf("%13I64u Restored : \"%s\"\n", data_size, ascii_buf); + } + } else { + bad_flag = 1; + if (data_size == 0){ // サイズが 0ならすぐに復元できる + need_repair++; + hFile = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE){ + bad_flag = 0; + need_repair += 0x10000; // 上位 16-bit に修復できた数を記録する + } + CloseHandle(hFile); + hFile = NULL; + } + if (bad_flag){ + if (parity_flag) + block_lost++; + printf(" - Missing : \"%s\"\n", ascii_buf); + } else { + if (parity_flag) + exist[source_num - 1] = 1; + printf("%13I64u Restored : \"%s\"\n", data_size, ascii_buf); + } + } + + if (parity_flag){ + // ソース・ファイルを開く + if (exist[source_num - 1]){ // ソース・ブロックが存在するなら + files[source_num - 1].hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (files[source_num - 1].hFile == INVALID_HANDLE_VALUE){ + exist[source_num - 1] = 0; + } else { + if (!GetFileSizeEx(files[source_num - 1].hFile, (PLARGE_INTEGER)&file_size2)){ + exist[source_num - 1] = 0; + } else { + files[source_num - 1].size = file_size2; + } + } + } else { // 破損してる、または存在しないなら + get_temp_name(file_path, find_path); // 修復中のテンポラリ・ファイル + files[source_num - 1].hFile = CreateFile(find_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (files[source_num - 1].hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + printf_cp("cannot create file, %s\n", find_path); + err = 1; + goto error_end; + } + files[source_num - 1].size = data_size; + } + //printf("%d: exist = %d, size = %I64u \n", source_num - 1, exist[source_num - 1], files[source_num - 1].size); + } + + fflush(stdout); + list_off += entry_size; // 次のエントリー位置にずらす + } + printf("\nData File lost\t: %d\n\n", block_lost); + + // 修復に必要なパリティ・ブロックの数が少なくて済むなら最大値を調節する + if (block_lost < parity_num){ + volume_num = 0; + len = block_max + parity_max; + for (i = block_max; i < len; i++){ + if (exist[i]){ + volume_num++; + if (volume_num > block_lost){ + volume_num--; + exist[i] = 0; + CloseHandle(files[i].hFile); + files[i].hFile = NULL; + } else { + parity_max = 1 + i - block_max; + } + } + } + len = sizeof(file_ctx) * (block_max + parity_max); + tmp_p = (file_ctx *)realloc(files, len); + if (tmp_p == NULL){ + printf("realloc, %d\n", len); + err = 1; + goto error_end; + } else { + files = tmp_p; + } + } + + if ((block_lost == 0) && (need_repair == 0)) + printf("All Files Complete\n"); + if (recovery_lost > 0){ // 不完全なリカバリ・ファイルがあるなら + err = 256; + printf("%d PAR File(s) Incomplete\n", recovery_lost); + } + + // 修復する必要があるかどうか + if (block_lost == 0){ + if (need_repair > 0){ + need_repair &= 0x3FFFFFFF; + printf("Restored file count\t: %d\n\n", need_repair >> 16); + need_repair = (need_repair & 0xFFFF) - (need_repair >> 16); + if (need_repair == 0){ // 全て修復できたのなら + printf("Repaired successfully\n"); + err |= 16; + } else { + printf("Failed to repair %d file(s)\n", need_repair); + err |= 16 | 4; + } + } + goto error_end; + } else { + if (need_repair > 0){ + printf("Restored file count\t: %d\n\n", (need_repair >> 16) & 0x3FFF); + if (need_repair & 0x40000000){ + need_repair = (need_repair & 0xFFFF) - ((need_repair >> 16) & 0x3FFF); + printf("Failed to repair %d file(s)\n", block_lost + need_repair); + err |= 16 | 4; + goto error_end; + } + need_repair &= 0x3FFFFFFF; + need_repair = (need_repair & 0xFFFF) - (need_repair >> 16); + } + if (block_lost > parity_num){ + printf("Need %d more volume(s) to repair %d file(s)\n", block_lost - parity_num, block_lost); + err |= 8 | 4; + goto error_end; + } else { + printf("Ready to repair %d file(s)\n", block_lost); + } + } + + // 失われたブロックを復元する + printf("\n"); + print_progress_text(0, "Recovering data"); + if (err = rs_decode(block_max, block_lost, block_size, parity_max, exist, files)){ + goto error_end; + } + + // 正しく修復できたか確かめる + printf("\nVerifying repair: %d\n", block_lost); + printf(" Status : Filename\n"); + fflush(stdout); + wcscpy(file_path, base_dir); + find_flag = 0; + source_num = 0; + memcpy(&list_off, header_buf + 64, 4); + for (i = 0; i < file_num; i++){ + bad_flag = 0; + + // entry size + memcpy(&entry_size, header_buf + list_off, 4); + // status field + memcpy(&parity_flag, header_buf + (list_off + 8), 2); + parity_flag &= 0x0001; // bit 0 = file is saved in the parity volume set + // size + memcpy(&data_size, header_buf + (list_off + 16), 8); + // filename + len = (entry_size - 56) / 2; + if (len >= MAX_LEN) + len = MAX_LEN - 1; + memcpy(file_name, header_buf + (list_off + 56), len * 2); + file_name[len] = 0; + utf16_to_cp(file_name, ascii_buf); + sanitize_filename(file_name); + + // ファイル名を基準ディレクトリに追加してパスにする + wcscpy(file_path + base_len, file_name); + + //printf("%d : list_off = %d, exist = %d \n", i, list_off, exist[source_num]); + // 修復したファイルのハッシュ値を計算する + if (parity_flag){ + if (exist[source_num] == 0){ + if (file_md5(file_name, files[source_num].hFile, data_size, hash)){ + bad_flag = 1; + } else { + for (j = 0; j < 16; j++){ + if (hash[j] != header_buf[list_off + 24 + j]){ + bad_flag = 1; + break; + } + } + } + CloseHandle(files[source_num].hFile); + files[source_num].hFile = NULL; + get_temp_name(file_path, find_path); + if (bad_flag){ // 失敗 + DeleteFile(find_path); // テンポラリ・ファイルを削除する + printf(" Failed : \"%s\"\n", ascii_buf); + } else { // 復元成功 + if (replace_file(file_path, find_path, switch_b) == 0){ // 修復したファイルを戻す + block_lost--; + find_flag++; + printf(" Repaired : \"%s\"\n", ascii_buf); + } else { // ファイルを戻せなかった場合は、テンポラリ・ファイルを残した方がいいかも? + // まあ PAR2 と違ってすぐに修復できるから、削除してもよさそう。 + DeleteFile(find_path); // テンポラリ・ファイルを削除する + printf(" Locked : \"%s\"\n", ascii_buf); + } + } + fflush(stdout); + } + source_num++; + } + + list_off += entry_size; // 次のエントリー位置にずらす + } + printf("\nRepaired file count\t: %d\n\n", find_flag); + + err = 16; + if (block_lost + need_repair == 0){ // 全て修復できたなら + printf("Repaired successfully\n"); + } else { + printf("Failed to repair %d file(s)\n", block_lost + need_repair); + err |= 4; + } + if (recovery_lost > 0) + err |= 256; + +error_end: + close_ini_file(); + if (files){ + // エラーが発生したら、作業中のファイルを削除する + source_num = 0; + memcpy(&list_off, header_buf + 64, 4); + for (i = 0; i < file_num; i++){ + // entry size + memcpy(&entry_size, header_buf + list_off, 4); + // status field + memcpy(&parity_flag, header_buf + (list_off + 8), 2); + parity_flag &= 0x0001; // bit 0 = file is saved in the parity volume set + // filename + len = (entry_size - 56) / 2; + if (len >= MAX_LEN) + len = MAX_LEN - 1; + memcpy(file_name, header_buf + (list_off + 56), len * 2); + file_name[len] = 0; + sanitize_filename(file_name); + + // ファイル名を基準ディレクトリに追加してパスにする + wcscpy(file_path + base_len, file_name); + + if (parity_flag){ + if ((exist[source_num] == 0) && (files[source_num].hFile)){ + CloseHandle(files[source_num].hFile); + files[source_num].hFile = NULL; + get_temp_name(file_path, find_path); + DeleteFile(find_path); // テンポラリ・ファイルを削除する + } + source_num++; + } + + list_off += entry_size; // 次のエントリー位置にずらす + } + for (i = 0; i < (source_num + parity_max); i++){ + if (files[i].hFile != NULL) + CloseHandle(files[i].hFile); + } + free(files); + } + if (hFile != NULL) + CloseHandle(hFile); + if (header_buf) + free(header_buf); + if (header_buf2) + free(header_buf2); + return err; +} + +// ソース・ファイルの一覧を表示する +int par1_list( + int switch_h, // ハッシュ値も表示する + wchar_t *par_comment) // コメント +{ + char ascii_buf[MAX_LEN * 3]; + unsigned char *header_buf = NULL, hash[16]; + wchar_t file_name[MAX_LEN]; + int err = 0, i, parity_flag; + int len, buf_size, entry_size, blk; + int volume_num, file_num, list_off, list_size, data_off; + int source_num = 0; + __int64 file_size, data_size, block_size = 0; + HANDLE hFile = NULL; + + // 指定されたリカバリ・ファイルを開く + hFile = CreateFile(recovery_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + printf("cannot open PAR file\n"); + err = 1; + goto error_end; + } + + // ファイルのサイズを取得する + if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){ + print_win32_err(); + err = 1; + goto error_end; + } + + // ヘッダー用のバッファーを確保する + buf_size = 0x0060 + ((0x0038 + (260 * 2)) * 256) + (COMMENT_LEN * 2); // 最大サイズ + if ((__int64)buf_size > file_size) + buf_size = (unsigned int)file_size; + header_buf = malloc(buf_size); + if (header_buf == NULL){ + printf("malloc, %d\n", buf_size); + err = 1; + goto error_end; + } + + // PAR や PXX ファイルとして正しいかを調べる + if (err = get_header(NULL, hFile, header_buf, buf_size, hash, + &volume_num, &file_num, &list_off, &list_size, &data_off, &data_size)){ + if (err != 2) // キャンセル以外なら + printf("valid file is not found\n"); + goto error_end; + } else { + if ((volume_num == 0) && (data_size > 0)){ // コメントがある + len = (int)data_size; + if (buf_size < (int)data_size) + len = buf_size; + if (len > 1) + read_comment(par_comment, header_buf + data_off, len); + if (!utf16_to_cp(par_comment, ascii_buf)) + printf("Comment : %s\n", ascii_buf); // コメントをユニコードから戻せた場合だけ表示する + } + } + CloseHandle(hFile); + hFile = NULL; + + printf("Input File list : %d\n", file_num); + if (switch_h){ + printf(" Size B MD5 Hash : Filename\n"); + } else { + printf(" Size B : Filename\n"); + } + // ソース・ファイルの情報を表示する + for (i = 0; i < file_num; i++){ + // entry size + memcpy(&entry_size, header_buf + list_off, 4); + // status field + memcpy(&parity_flag, header_buf + (list_off + 8), 2); + parity_flag &= 0x01; // bit 0 = file is saved in the parity volume set + if (parity_flag){ + source_num++; + blk = 1; + } else { + blk = 0; + } + // size + memcpy(&file_size, header_buf + (list_off + 16), 8); + if (file_size > block_size) + block_size = file_size; + // hash + memcpy(hash, header_buf + (list_off + 40), 16); + // filename + len = (entry_size - 56) / 2; + if (len >= MAX_LEN) + len = MAX_LEN - 1; + memcpy(file_name, header_buf + (list_off + 56), len * 2); + file_name[len] = 0; + utf16_to_cp(file_name, ascii_buf); + + // 項目番号、ソース・ブロックかどうか、ファイル・サイズ、MD5ハッシュ値、ファイル名 + if (switch_h){ + printf("%13I64u %d ", file_size, blk); + print_hash(hash); + printf(" : \"%s\"\n", ascii_buf); + } else { + printf("%13I64u %d : \"%s\"\n", file_size, blk, ascii_buf); + } + + list_off += entry_size; // 次のエントリー位置にずらす + } + + // 調べた結果を表示する + printf("\nData File count : %d\n", source_num); + printf("Max file size\t: %I64u\n", block_size); + printf("\nListed successfully\n"); +error_end: + if (header_buf) + free(header_buf); + if (hFile != NULL) + CloseHandle(hFile); + return err; +} + +// CRC-32 チェックサムを使って自分自身の破損を検出する +int par1_checksum(wchar_t *uni_buf) // 作業用 +{ + unsigned int crc, chk, chk2; + unsigned int len, tmp, i; + unsigned char *pAddr, *p; + HANDLE hFile, hMap; + + // 実行ファイルのパスを取得する + tmp = GetModuleFileName(NULL, uni_buf, MAX_LEN); + if ((tmp == 0) || (tmp >= MAX_LEN)) + return 1; + //printf("%S\n", uni_buf); + + // 実行ファイルの PE checksum と CRC-32 を検証する + hFile = CreateFile(uni_buf, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE){ + print_win32_err(); + return 1; + } + len = GetFileSize(hFile, &chk2); + if (len == INVALID_FILE_SIZE) + return 1; + hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, chk2, len, NULL); + if (hMap == NULL){ + CloseHandle(hFile); + return 1; + } + pAddr = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, len); + if (pAddr == NULL){ + CloseHandle(hMap); + CloseHandle(hFile); + return 1; + } + if (CheckSumMappedFile(pAddr, len, &chk2, &chk) == NULL){ // PE checksum + UnmapViewOfFile(pAddr); + CloseHandle(hMap); + CloseHandle(hFile); + return 1; + } + crc = 0xFFFFFFFF; + p = pAddr; + while (len--){ + tmp = (*p++); + for (i = 0; i < 8; i++){ + if ((tmp ^ crc) & 1){ + crc = (crc >> 1) ^ 0xEDB88320; + } else { + crc = crc >> 1; + } + tmp = tmp >> 1; + } + } + crc ^= 0xFFFFFFFF; + UnmapViewOfFile(pAddr); + CloseHandle(hMap); + CloseHandle(hFile); + + if (chk != chk2) + return 2; + if (crc != 0x11111111) + return 3; + return 0; +} + diff --git a/source/par1j/par1.h b/source/par1j/par1.h new file mode 100644 index 0000000..1d8fa3c --- /dev/null +++ b/source/par1j/par1.h @@ -0,0 +1,46 @@ +#ifndef _PAR1_H_ +#define _PAR1_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +// リカバリ・ファイルを作成する +int par1_create( + int switch_p, // インデックス・ファイルを作らない + int block_num, // ソース・ブロックの数 + __int64 block_size, // ブロック・サイズ (最大ファイル・サイズ) + int parity_num, // パリティ・ブロックの数 + int first_vol, // 最初のリカバリ・ファイル番号 + int file_num, // ソース・ファイルの数 + wchar_t *list_buf, // ソース・ファイルのリスト + int list_len, // ファイル・リストの文字数 + wchar_t *par_comment); // コメント + +// ソース・ファイルの破損や欠損を調べる +int par1_verify( + int switch_b, // 既存のファイルを別名にしてどかす + int switch_p, // インデックス・ファイルを作り直す + wchar_t *par_comment); // コメント + +// ソース・ファイルの破損や欠損を修復する +int par1_repair( + int switch_b, // 既存のファイルを別名にしてどかす + int switch_p, // インデックス・ファイルを作り直す + wchar_t *par_comment); // コメント + +// ソース・ファイルの一覧を表示する +int par1_list( + int switch_h, // ハッシュ値も表示する + wchar_t *par_comment); // コメント + +// CRC-32 チェックサムを使って自分自身の破損を検出する +int par1_checksum(wchar_t *uni_buf); // 作業用 + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/par1j/par1_cmd.c b/source/par1j/par1_cmd.c new file mode 100644 index 0000000..05b2769 --- /dev/null +++ b/source/par1j/par1_cmd.c @@ -0,0 +1,753 @@ +// par1_cmd.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 "common1.h" +#include "par1.h" +#include "ini.h" +#include "version.h" + + +void print_help(void) +{ + printf( +"Usage\n" +"c(reate) [f,fu,r,n,p,m,c,d,in,u] [input files]\n" +"v(erify) [ vs,vd,d,i,u,b,br] \n" +"r(epair) [m,vs,vd,d,i,u,b,br] \n" +"l(ist) [u,h] \n" +"\nOption\n" +" /f : Use file-list instead of files\n" +" /fu : Use file-list which is encoded with UTF-8\n" +" /r : Rate of redundancy (%%)\n" +" /n : Number of parity volumes\n" +" /p : First parity volume number\n" +" /m : Memory usage\n" +" /vs: Skip verification by recent result\n" +" /vd\"*\": Set directory of recent result\n" +" /c\"*\" : Set comment\n" +" /d\"*\" : Set directory of input files\n" +" /i : Recreate index file\n" +" /in : Do not create index file\n" +" /u : Console output is encoded with UTF-8\n" +" /b : Backup existing files at repair\n" +" /br : Send existing files into recycle bin at repair\n" +" /h : List hash value of input files\n" + ); +} + +// 動作環境の表示 +static void print_environment(void) +{ + MEMORYSTATUSEX statex; + + // 「\\?\」は常に付加されてるので表示する際には無視する + printf_cp("Base Directory\t: \"%s\"\n", base_dir); + printf_cp("Recovery File\t: \"%s\"\n", recovery_file); + + printf("Memory usage\t: "); + if (memory_use == 0){ + printf("Auto"); + } else { + printf("%d/8", memory_use); + } + statex.dwLength = sizeof(statex); + if (GlobalMemoryStatusEx(&statex)) + printf(" (%I64d MB available)", statex.ullAvailPhys >> 20); + printf("\n\n"); +} + +// 格納ファイル・リストを読み込んでバッファーに書き込む +static wchar_t * read_list( + wchar_t *list_path, // リストのパス + int *file_num, // データ・ファイルの数 + int *list_len, // ファイル・リストの文字数 + int *block_num, // ソース・ブロックの数 + __int64 *block_size, // ブロック・サイズ (最大ファイル・サイズ) + int switch_f) // 1=CP_OEMCP, 2=CP_UTF8 +{ + char buf[MAX_LEN * 3]; + wchar_t *list_buf, file_name[MAX_LEN], file_path[MAX_LEN], *tmp_p; + int len, base_len, l_max, l_off = 0; + __int64 file_size; + FILE *fp; + WIN32_FILE_ATTRIBUTE_DATA AttrData; + + base_len = wcslen(base_dir); + if (switch_f == 2){ + switch_f = CP_UTF8; + } else { + switch_f = CP_OEMCP; + } + l_max = ALLOC_LEN; + list_buf = malloc(l_max); + if (list_buf == NULL){ + printf("malloc, %d\n", l_max); + return NULL; + } + + // 読み込むファイルを開く + fp = _wfopen(list_path, L"rb"); + if (fp == NULL){ + utf16_to_cp(list_path, buf); + free(list_buf); + printf("cannot open file-list, %s\n", buf); + return NULL; + } + + // 一行ずつ読み込む + while (fgets(buf, MAX_LEN * 3, fp)){ + if (ferror(fp)) + break; + buf[MAX_LEN * 3 - 1] = 0; + // 末尾に改行があれば削除する + for (len = 0; len < MAX_LEN * 3; len++){ + if (buf[len] == 0) + break; + if ((buf[len] == '\n') || (buf[len] == '\r')){ + buf[len] = 0; + break; + } + } + if (buf[0] == 0) + continue; // 改行だけなら次の行へ + + // 読み込んだ内容をユニコードに変換する + if (!MultiByteToWideChar(switch_f, 0, buf, -1, file_name, MAX_LEN)){ + fclose(fp); + free(list_buf); + printf("MultiByteToWideChar, %s\n", buf); + return NULL; + } + + // ファイルが基準ディレクトリ以下に存在することを確認する + len = copy_path_prefix(file_path, MAX_LEN - ADD_LEN, file_name, base_dir); // 絶対パスにしてから比較する + if (len == 0){ + fclose(fp); + free(list_buf); + printf_cp("filename is invalid, %s\n", file_name); + return NULL; + } + if ((len <= base_len) || (_wcsnicmp(base_dir, file_path, base_len) != 0)){ // 基準ディレクトリ外なら + fclose(fp); + free(list_buf); + printf("out of base-directory, %s\n", buf); + return NULL; + } + wcscpy(file_name, file_path + base_len); + if (wcschr(file_name, '\\') != NULL){ // サブ・ディレクトリは拒否する + fclose(fp); + free(list_buf); + printf_cp("sub-directory is not supported, %s\n", file_name); + return NULL; + } + + // ファイルが存在するか確かめる + if (!GetFileAttributesEx(file_path, GetFileExInfoStandard, &AttrData)){ + print_win32_err(); + fclose(fp); + free(list_buf); + printf("input file is not found, %s\n", buf); + return NULL; + } + if (AttrData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){ + fclose(fp); + free(list_buf); + printf("folder is not supported, %s\n", buf); + return NULL; + } + file_size = ((unsigned __int64)AttrData.nFileSizeHigh << 32) | (unsigned __int64)AttrData.nFileSizeLow; + if (*block_size < file_size) + *block_size = file_size; + + // ファイル名が重複しないようにする + if (search_file_path(list_buf, l_off, file_name)) + continue; + + // バッファー容量が足りなければ再確保する + len = wcslen(file_name); + if ((l_off + len) * 2 >= l_max){ + l_max += ALLOC_LEN; + tmp_p = (wchar_t *)realloc(list_buf, l_max); + if (tmp_p == NULL){ + free(list_buf); + printf("realloc, %d\n", l_max); + return NULL; + } else { + list_buf = tmp_p; + } + } + + // リストにコピーする + wcscpy(list_buf + l_off, file_name); + l_off += (len + 1); + *file_num += 1; + if (file_size > 0) + *block_num += 1; // 空のファイルはブロックの計算に含めない + } + *list_len = l_off; + fclose(fp); +/* +fp = fopen("list_buf.txt", "wb"); +len = 0; +while (len < l_off){ + fwprintf(fp, L"%s\n", list_buf + len); + len += (wcslen(list_buf + len) + 1); +} +fclose(fp); +*/ + return list_buf; +} + +// ファイルを検索してファイル・リストに追加する +static wchar_t * search_files( + wchar_t *list_buf, // ファイル・リスト + wchar_t *search_path, // 検索するファイルのフル・パス + int dir_len, // ディレクトリ部分の長さ + int single_file, // -1 = *や?で検索指定、0~ = 単独指定 + int *file_num, // データ・ファイルの数 + int *list_max, // ファイル・リストの確保サイズ + int *list_len, // ファイル・リストの文字数 + int *block_num, // ソース・ブロックの数 + __int64 *block_size) // ブロック・サイズ (最大ファイル・サイズ) +{ + wchar_t *tmp_p; + int len, l_max, l_off; + __int64 file_size; + HANDLE hFind; + WIN32_FIND_DATA FindData; + + if (list_buf == NULL){ + l_off = 0; + l_max = ALLOC_LEN; + list_buf = malloc(l_max); + if (list_buf == NULL){ + printf("malloc, %d\n", l_max); + return NULL; + } + } else { + l_max = *list_max; + l_off = *list_len; + } + + // 検索する + hFind = FindFirstFile(search_path, &FindData); + if (hFind == INVALID_HANDLE_VALUE) + return list_buf; // 見つからなかったらそのまま + do { + if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + continue; // フォルダは無視する + + if ((single_file < 0) && (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) + continue; // 検索中は隠し属性が付いてるファイルを無視する + + // フォルダは無視する、ファイル名が重複しないようにする + if (!search_file_path(list_buf, l_off, FindData.cFileName)){ + //printf("FindData.cFileName =\n%S\n\n", FindData.cFileName); + len = wcslen(FindData.cFileName); + if (dir_len + len >= MAX_LEN - ADD_LEN){ + FindClose(hFind); + free(list_buf); + printf("filename is too long\n"); + return NULL; + } + if ((l_off + len) * 2 >= l_max){ // 領域が足りなくなるなら拡張する + l_max += ALLOC_LEN; + tmp_p = (wchar_t *)realloc(list_buf, l_max); + if (tmp_p == NULL){ + FindClose(hFind); + free(list_buf); + printf("realloc, %d\n", l_max); + return NULL; + } else { + list_buf = tmp_p; + } + } + + file_size = ((unsigned __int64)FindData.nFileSizeHigh << 32) | (unsigned __int64)FindData.nFileSizeLow; + if (*block_size < file_size) + *block_size = file_size; + + // リストにコピーする + wcscpy(list_buf + l_off, FindData.cFileName); + l_off += len + 1; + *file_num += 1; + if (file_size > 0) + *block_num += 1; // 空のファイルはブロックの計算に含めない + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + + *list_max = l_max; + *list_len = l_off; + return list_buf; +} + +// リカバリ・ファイルとソース・ファイルが同じ名前にならないようにする +static int check_recovery_match( + int file_num, + wchar_t *list_buf, + int list_len, + int switch_p) // インデックス・ファイルを作らない +{ + wchar_t *tmp_p; + int i, num, len, list_off = 0, recovery_len, base_len; + + // 基準ディレクトリ外にリカバリ・ファイルが存在するなら問題なし + base_len = wcslen(base_dir); + if (_wcsnicmp(recovery_file, base_dir, base_len) != 0) + return 0; + + // リカバリ・ファイル (*.PXX) の名前の基 + recovery_len = wcslen(recovery_file); + recovery_len -= base_len + 2; // 末尾の 2文字を除くファイル名の長さ + + // ファイルごとに比較する + for (num = 0; num < file_num; num++){ + tmp_p = list_buf + list_off; + while (list_buf[list_off] != 0) + list_off++; + list_off++; + + // ファイル名との前方一致を調べる + if (_wcsnicmp(recovery_file + base_len, tmp_p, recovery_len) == 0){ // リカバリ・ファイルと基準が同じ + len = wcslen(tmp_p); + // 拡張子も同じか調べる + if (len == recovery_len + 2){ + // インデックス・ファイルと比較する + if (switch_p == 0){ + if (_wcsicmp(tmp_p + recovery_len, L"ar") == 0){ + printf_cp("filename is invalid, %s\n", tmp_p); + return 1; + } + } + // リカバリ・ファイル (*.PXX) と比較する + for (i = 1; i <= 99; i++){ + if ((tmp_p[recovery_len ] == '0' + (i / 10)) && + (tmp_p[recovery_len + 1] == '0' + (i % 10))){ + printf_cp("filename is invalid, %s\n", tmp_p); + return 1; + } + } + } + } + } + return 0; +} + +wmain(int argc, wchar_t *argv[]) +{ + wchar_t file_path[MAX_LEN], *list_buf; + wchar_t par_comment[COMMENT_LEN], *tmp_p; + int i, j; + int switch_b = 0, switch_p = 0, switch_f = 0, switch_r = 0, switch_h = 0; + int file_num = 0, source_num = 0, list_len = 0, parity_num = 0, first_vol = 1; + __int64 block_size = 0; + + printf("Parchive 1.0 client version " FILE_VERSION " by Yutaka Sawada\n\n"); + if (argc < 3){ + printf("Self-Test: "); + i = par1_checksum(file_path); + if (i == 0){ + printf("Success"); + } else if (i == 2){ + printf("PE checksum is different"); + } else if (i == 3){ + printf("CRC-32 is different"); + } else { + printf("Error\0thedummytext"); + } + printf("\n\n"); + print_help(); + return 0; + } + + // 初期化 + recovery_file[0] = 0; + par_comment[0] = 0; + base_dir[0] = 0; + ini_path[0] = 0; + cp_output = GetConsoleOutputCP(); + memory_use = 0; + + // コマンド + switch (argv[1][0]){ + case 'c': // create + case 'v': // verify + case 'r': // repair + case 'l': // list + break; + default: + print_help(); + return 0; + } + + // オプションとリカバリ・ファイルの指定 + for (i = 2; i < argc; i++){ + tmp_p = argv[i]; + // オプション + if (((tmp_p[0] == '/') || (tmp_p[0] == '-')) && (tmp_p[0] == argv[2][0])){ + tmp_p++; // 先頭の識別文字をとばす + // オプション + if (wcscmp(tmp_p, L"b") == 0){ + switch_b = 1; + } else if (wcscmp(tmp_p, L"br") == 0){ + switch_b = 2; + } else if (wcscmp(tmp_p, L"f") == 0){ + switch_f = 1; + } else if (wcscmp(tmp_p, L"fu") == 0){ + switch_f = 2; + } else if (wcscmp(tmp_p, L"i") == 0){ + switch_p = 1; + } else if (wcscmp(tmp_p, L"in") == 0){ + switch_p = 2; + } else if (wcscmp(tmp_p, L"u") == 0){ + cp_output = CP_UTF8; + } else if (wcscmp(tmp_p, L"h") == 0){ + switch_h = 1; + + // オプション (数値) + } else if (wcsncmp(tmp_p, L"n", 1) == 0){ + j = 1; + while ((j < 1 + 4) && (tmp_p[j] >= 48) && (tmp_p[j] <= 57)){ + parity_num = (parity_num * 10) + (tmp_p[j] - 48); + j++; + } + } else if (wcsncmp(tmp_p, L"r", 1) == 0){ + j = 1; + while ((j < 1 + 5) && (tmp_p[j] >= 48) && (tmp_p[j] <= 57)){ + switch_r = (switch_r * 10) + (tmp_p[j] - 48); + j++; + } + } else if (wcsncmp(tmp_p, L"p", 1) == 0){ + j = 1; + first_vol = 0; + while ((j < 1 + 4) && (tmp_p[j] >= 48) && (tmp_p[j] <= 57)){ + first_vol = (first_vol * 10) + (tmp_p[j] - 48); + j++; + } + if (first_vol < 1) + first_vol = 1; + if (first_vol > 99) + first_vol = 99; + } else if (wcsncmp(tmp_p, L"m", 1) == 0){ + if ((tmp_p[1] >= 48) && (tmp_p[1] <= 55)) // 0~7 の範囲 + memory_use = tmp_p[1] - 48; + } else if (wcsncmp(tmp_p, L"vs", 2) == 0){ + recent_data = 0; + j = 2; + while ((j < 2 + 2) && (tmp_p[j] >= '0') && (tmp_p[j] <= '9')){ + recent_data = (recent_data * 10) + (tmp_p[j] - '0'); + j++; + } + if ((recent_data == 8) || (recent_data > 15)) + recent_data = 0; + + // オプション (文字列) + } else if (wcsncmp(tmp_p, L"c", 1) == 0){ + tmp_p++; + if (wcslen(tmp_p) >= COMMENT_LEN){ + printf("comment is too long\n"); + return 1; + } + wcscpy(par_comment, tmp_p); + //printf("comment = %S\n", tmp_p); + } else if (wcsncmp(tmp_p, L"vd", 2) == 0){ + tmp_p += 2; + j = copy_path_prefix(ini_path, MAX_LEN - INI_NAME_LEN - 1, tmp_p, NULL); + if (j == 0){ + printf("save-directory is invalid\n"); + return 1; + } + if (ini_path[j - 1] != '\\'){ // 末尾が「\」でなければ付けておく + ini_path[j ] = '\\'; + ini_path[j + 1] = 0; + } + //printf("Save Directory : %S\n", ini_path); + j = GetFileAttributes(ini_path); + if ((j == INVALID_FILE_ATTRIBUTES) || !(j & FILE_ATTRIBUTE_DIRECTORY)){ + printf("save-directory is invalid\n"); + return 1; + } + } else if (wcsncmp(tmp_p, L"d", 1) == 0){ + tmp_p++; + j = copy_path_prefix(base_dir, MAX_LEN - 2, tmp_p, NULL); // 末尾に追加される分の余裕を見ておく + if (j == 0){ + printf("base-directory is invalid\n"); + return 1; + } + if (base_dir[j - 1] != '\\'){ // 末尾が「\」でなければ付けておく + base_dir[j ] = '\\'; + base_dir[j + 1] = 0; + } + j = GetFileAttributes(base_dir); + if ((j == INVALID_FILE_ATTRIBUTES) || !(j & FILE_ATTRIBUTE_DIRECTORY)){ + printf("base-directory is invalid\n"); + return 1; + } + + } else { // 未対応のオプション + printf_cp("invalid option, %s\n", tmp_p - 1); + return 1; + } + continue; + } + // オプション以外ならリカバリ・ファイル + j = copy_path_prefix(recovery_file, MAX_LEN - 4, tmp_p, NULL); // 「.par」が追加されるかも + if (j == 0){ + printf("PAR filename is invalid\n"); + return 1; + } + if (argv[1][0] == 'c'){ // 作成なら + // 指定されたファイル名が適切か調べる + if (sanitize_filename(offset_file_name(recovery_file)) != 0){ + printf("PAR filename is invalid\n"); + return 1; + } + // 拡張子が「.par」以外なら標準の拡張子を追加する + j = wcslen(recovery_file); // 浄化でファイル名が短縮されるかもしれない + if (_wcsicmp(recovery_file + (j - 4), L".par")) + wcscpy(recovery_file + j, L".par"); + } else { // 検査や修復なら + if (_wcsicmp(recovery_file + (j - 4), L".par")){ + // 拡張子が「.par」以外でも「.p??」ならよい + if ((recovery_file[j - 4] != '.') || + ((recovery_file[j - 3] != 'p') && (recovery_file[j - 3] != 'P')) || + (recovery_file[j - 2] < 48) || (recovery_file[j - 2] > 57) || + (recovery_file[j - 1] < 48) || (recovery_file[j - 1] > 57) ){ + wcscpy(recovery_file + j, L".par"); + } + } + j = GetFileAttributes(recovery_file); + if ((j == INVALID_FILE_ATTRIBUTES) || (j & FILE_ATTRIBUTE_DIRECTORY)){ + wchar_t search_path[MAX_LEN]; + int name_len, dir_len, path_len, find_flag = 0; + HANDLE hFind; + WIN32_FIND_DATA FindData; + path_len = wcslen(recovery_file); + if (wcspbrk(offset_file_name(recovery_file), L"*?") != NULL){ + // 「*」や「?」で検索する場合は指定された拡張子を優先する + hFind = FindFirstFile(recovery_file, &FindData); + if (hFind != INVALID_HANDLE_VALUE){ + get_base_dir(recovery_file, search_path); + dir_len = wcslen(search_path); + do { + //printf("file name = %S\n", FindData.cFileName); + name_len = wcslen(FindData.cFileName); + if ((dir_len + name_len < MAX_LEN) && // ファイル名が長すぎない + (_wcsicmp(FindData.cFileName + (name_len - 4), recovery_file + (path_len - 4)) == 0)){ + find_flag = 1; + break; // 見つけたファイル名で問題なし + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + } + } + if (find_flag == 0){ // リカバリ・ファイルの拡張子を「.p??」にして検索する + wcscpy(search_path, recovery_file); + search_path[path_len - 2] = '?'; + search_path[path_len - 1] = '?'; + hFind = FindFirstFile(search_path, &FindData); + if (hFind != INVALID_HANDLE_VALUE){ + get_base_dir(recovery_file, search_path); + dir_len = wcslen(search_path); + do { + //printf("file name = %S\n", FindData.cFileName); + name_len = wcslen(FindData.cFileName); + if ((dir_len + name_len < MAX_LEN) && // ファイル名が長すぎない + (FindData.cFileName[name_len - 4] == '.') && + ((FindData.cFileName[name_len - 3] == 'p') || (FindData.cFileName[name_len - 3] == 'P')) && + (FindData.cFileName[name_len - 2] >= '0') && (FindData.cFileName[name_len - 2] <= '9') && + (FindData.cFileName[name_len - 1] >= '0') && (FindData.cFileName[name_len - 1] <= '9')){ + find_flag = 1; + break; // 見つけたファイル名で問題なし + } + } while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する + FindClose(hFind); + } + } + if (find_flag == 0){ + printf("valid file is not found\n"); + return 1; + } + wcscpy(recovery_file + dir_len, FindData.cFileName); + } + } + break; + } + //printf("m = %d, f = %d, p = %d, n = %d\n", switch_b, switch_f, switch_p, parity_num); + if (recovery_file[0] == 0){ // リカバリ・ファイルが指定されてないなら + printf("PAR file is not specified\n"); + return 1; + } + + // input file の位置が指定されて無くて、 + // 最初のソース・ファイル指定に絶対パスが含まれてるなら、それを使う + if (argv[1][0] == 'c'){ + if ((base_dir[0] == 0) && (switch_f == 0) && (i + 1 < argc)){ + tmp_p = argv[i + 1]; + if (is_full_path(tmp_p) != 0){ // 絶対パスなら + if (copy_path_prefix(file_path, MAX_LEN - 2, tmp_p, NULL) == 0){ + printf("base-directory is invalid\n"); + return 1; + } + get_base_dir(file_path, base_dir); // 最初のソース・ファイルの位置にする + if (len_without_prefix(base_dir) == 0) // サブ・ディレクトリが無ければ + base_dir[0] = 0; + } + } + } + if (base_dir[0] == 0) // input file の位置が指定されてないなら + get_base_dir(recovery_file, base_dir); // リカバリ・ファイルの位置にする + + // 環境の表示 + print_environment(); + + switch (argv[1][0]){ + case 'c': + // リカバリ・ファイル作成ならソース・ファイルのリストがいる + i++; + if (i >= argc){ + printf("input file is not specified\n"); + return 1; + } + if (switch_f){ // ファイル・リストの読み込み + list_buf = read_list(argv[i], &file_num, &list_len, &source_num, &block_size, switch_f); + if (list_buf == NULL) + return 1; + } else { // 入力ファイルの指定 + int base_len, list_max; + base_len = wcslen(base_dir); + list_max = 0; + list_buf = NULL; + for (; i < argc; i++){ + // 絶対パスや、親パスへの移動を含むパスは危険なのでファイル名だけにする + tmp_p = argv[i]; + j = copy_path_prefix(file_path, MAX_LEN - ADD_LEN, tmp_p, base_dir); // 絶対パスにしてから比較する + if (j == 0){ + free(list_buf); + printf_cp("filename is invalid, %s\n", tmp_p); + return 1; + } + if ((j <= base_len) || (_wcsnicmp(base_dir, file_path, base_len) != 0)){ // 基準ディレクトリ外なら + free(list_buf); + printf_cp("out of base-directory, %s\n", tmp_p); + return 1; + } + if (wcschr(file_path + base_len, '\\') != NULL){ + free(list_buf); + printf_cp("sub-directory is not supported, %s\n", file_path + base_len); + return 1; + } + //printf("%d = %S\nsearch = %S\n", i, argv[i], file_path); + // 「*」や「?」で検索しない場合、ファイルが見つからなければエラーにする + j = -1; + if (wcspbrk(file_path + base_len, L"*?") == NULL) + j = file_num; + // ファイルを検索する + list_buf = search_files(list_buf, file_path, base_len, j, &file_num, &list_max, &list_len, &source_num, &block_size); + if (list_buf == NULL) + return 1; + if ((j != -1) && (j == file_num)){ // ファイルが見つかったか確かめる + free(list_buf); + printf_cp("input file is not found, %s\n", file_path + base_len); + return 1; + } + } + } + if (list_len == 0){ + if (list_buf) + free(list_buf); + printf("input file is not found\n"); + return 1; + } + if (check_recovery_match(file_num, list_buf, list_len, switch_p & 2)){ + free(list_buf); + return 1; + } + + // ファイル・リストの内容を並び替える (リストの末尾は null 1個にすること) + sort_list(list_buf, list_len); + //printf("file_num = %d, source_num = %d, block_size = %I64d\n", file_num, source_num, block_size); + + if ((parity_num == 0) && (switch_r > 0)){ + parity_num = (source_num * switch_r) / 100; + if (parity_num == 0) + parity_num = 1; + } + printf("Input File count: %d\n", file_num); + printf("Data File count : %d\n", source_num); + printf("Max file size\t: %I64d\n", block_size); + printf("Parity Volume count\t: %d\n", parity_num); + if (first_vol > 1) + printf("Parity Volume start\t: %d\n", first_vol); + if (source_num != 0){ + i = 100 * parity_num / source_num; + } else { + i = 0; + } + printf("Redundancy rate : %d%%\n", i); + if (source_num + parity_num + first_vol - 1 > 256){ + free(list_buf); + printf("too many total volumes %d\n", source_num + parity_num + first_vol - 1); + return 1; + } + if (parity_num + first_vol - 1 > 99){ + free(list_buf); + printf("too many parity volumes %d\n", parity_num + first_vol - 1); + return 1; + } + i = par1_create(switch_p & 2, source_num, block_size, parity_num, first_vol, + file_num, list_buf, list_len, par_comment); + break; + case 'v': + case 'r': + // 検査結果ファイルの位置が指定されてないなら + if ((recent_data != 0) && (ini_path[0] == 0)){ + // 実行ファイルのディレクトリにする + j = GetModuleFileName(NULL, ini_path, MAX_LEN); + if ((j == 0) || (j >= MAX_LEN)){ + printf("GetModuleFileName\n"); + return 1; + } + while (j > 0){ + if (ini_path[j - 1] == '\\'){ + ini_path[j] = 0; + break; + } + j--; + } + if (j >= MAX_LEN - INI_NAME_LEN - 1){ + printf("save-directory is invalid\n"); + return 1; + } + } + if (argv[1][0] == 'v'){ + i = par1_verify(switch_b, switch_p & 1, par_comment); + } else { + i = par1_repair(switch_b, switch_p & 1, par_comment); + } + break; + case 'l': + i = par1_list(switch_h, par_comment); + break; + } + + //printf("ExitCode: 0x%02X\n", i); + return i; +} + diff --git a/source/par1j/phmd5.c b/source/par1j/phmd5.c new file mode 100644 index 0000000..415ab31 --- /dev/null +++ b/source/par1j/phmd5.c @@ -0,0 +1,79 @@ +/*---------------------------------------------------------------------------- +; +; MD5 hash generator -- Paul Houle (paulhoule.com) 11/13/2017 +; +; Non-time critical C logic. All API entry points are here. +; See phmd5.h for documentation. +; +;---------------------------------------------------------------------------*/ + +#include +#include "phmd5.h" + +// First call -- initialize pmd5 structure for use. +void Phmd5Begin(PHMD5 *pmd5) { + unsigned __int32 *uhash = (unsigned __int32 *) pmd5->hash; + + uhash[0] = 0x67452301; // init hash per rfc1321 + uhash[1] = 0xEFCDAB89; + uhash[2] = 0x98BADCFE; + uhash[3] = 0x10325476; + + pmd5->totbyt = 0; // init count of data bytes processed +} + +// Last call -- after this, pmd5->hash holds final MD5 hash. +void Phmd5End(PHMD5 *pmd5) { + char pad[72]; // pad buffer (worst case is 72 bytes) + unsigned padc; // size of needed pad (9-72 bytes) + + padc = 64 - ((unsigned) pmd5->totbyt & 63); // pad to 64-byte boundary + if (padc < 9) padc += 64; // add a block if we need more room + memset(pad, 0, padc); // clear entire pad area + pad[0] = (char) 0x80; // place input stream terminator + // place 64-bit input data bit count + *(unsigned __int64 *) &pad[padc - 8] = pmd5->totbyt << 3; + Phmd5Process(pmd5, pad, padc); // process the pad +} + +// Work done here -- call for as many input blocks that need to be processed. +// pdata points to the input data, bytecnt is pdata size (0..n bytes). +// See phmd5.h regarding how to use this optimally. +void Phmd5Process(PHMD5 *pmd5, char *pdata, size_t bytecnt) { + unsigned resid = (unsigned) pmd5->totbyt; + + pmd5->totbyt += bytecnt; // update total bytes processed + + resid &= 63; // count of bytes now in pmd5->buf + + // This block handles the case of residual data in pmd5->buf. + // After this block pmd5->buf is empty (except perhaps on exit). + + if (resid) { // if residual exists, + unsigned cb = 64 - resid; + if (cb > bytecnt) cb = (unsigned) bytecnt; + memcpy(pmd5->buf + resid, pdata, cb); + pdata += cb; + bytecnt -= cb; + if (resid + cb < 64) return; + Phmd5DoBlocks(pmd5->hash, pmd5->buf, 64); + } + + // This block processes input data in-place, if the data is dword + // aligned and in 64-byte chunks. + + if ((unsigned) bytecnt & ~63 && ((size_t) pdata & 3) == 0) { + Phmd5DoBlocks(pmd5->hash, pdata, bytecnt & ~63); + pdata += bytecnt & ~63; + bytecnt &= 63; + } + + while (bytecnt) { // handle residual/non-aligned data + unsigned cb = 64 > (unsigned) bytecnt ? (unsigned) bytecnt : 64; + memcpy(pmd5->buf, pdata, cb); + pdata += cb; + bytecnt -= cb; + if (cb < 64) return; + Phmd5DoBlocks(pmd5->hash, pmd5->buf, 64); + }; +} diff --git a/source/par1j/phmd5.h b/source/par1j/phmd5.h new file mode 100644 index 0000000..e43a19c --- /dev/null +++ b/source/par1j/phmd5.h @@ -0,0 +1,59 @@ +/*---------------------------------------------------------------------------- +; +; MD5 hash generator -- Paul Houle (paulhoule.com) 11/13/2017 +; +; This code is in the public domain. Please attribute the author. +; +; There are a lot of MD5 generators; here's another. This one targets a +; little-endian memory architecture only (eg X86). The benefit of this +; is speed -- bytes within larger elements never need to be reversed, +; which means the source data can be processed in-place. +; +; Though other compilers might be usable, this was developed using +; Microsoft 32/64-bit C 12.0 [Version 18.00.30723]. Vendor specific +; definitions (eg. _rotl, __int32, __int64) are used. +; Build commands: +; +; cl /c /Ox phmd5.c +; cl /c /Ox phmd5a.c +; +; Link the resulting .obj's into your executable and #include "phmd5.h" +; +; How to call the routines to generate a hash: +; +; (1) Allocate a PHMD5 type struct -- it's small, can be static or local. +; A pointer to this struct is the first argument to all functions. +; +; (2) Call Phmd5Begin() once -- this initializes the PHMD5 struct. +; +; (3) Call Phmd5Process() as many times as necessary for all data +; to be included in the MD5 hash. +; +; (4) Call Phmd5End() once. The final 16-byte MD5 hash will then be +; available in PHMD5->hash. Note the finished hash is a simple array +; of bytes, and must be treated/displayed/copied/etc that way. +; +; For best performance the Phmd5Process() "pdata" pointer should be 32-bit +; aligned (a multiple of 4) and "bytecnt" should be a multiple of 64. +; As long as both of these conditions continue to be met the input data is +; processed in-place; otherwise, some speed (10-15%) is lost as the data +; is copied to an internal blocking buffer before being proceessed. +; +;---------------------------------------------------------------------------*/ + +#ifndef _PHMD5_DEFINED // include guard +#define _PHMD5_DEFINED + +#include +typedef struct { + unsigned char hash[16]; // final 16-byte hash winds up here + unsigned __int64 totbyt; // processed byte count + char buf[64]; // input blocking buffer +} PHMD5; + +void Phmd5Begin(PHMD5 *pmd5); +void Phmd5Process(PHMD5 *pmd5, char *pdata, size_t bytecnt); +void Phmd5End(PHMD5 *pmd5); +void Phmd5DoBlocks(unsigned char *hash, char *pdata, size_t bytecnt); + +#endif diff --git a/source/par1j/phmd5a.c b/source/par1j/phmd5a.c new file mode 100644 index 0000000..f36e464 --- /dev/null +++ b/source/par1j/phmd5a.c @@ -0,0 +1,130 @@ +/*---------------------------------------------------------------------------- +; +; MD5 hash generator -- Paul Houle (paulhoule.com) 11/13/2017 +; +; Called only from phmd5.c -- see phmd5.h for overview +; +; This is the same logic in phmd5a.asm, implemented (after the fact) in C. +; The C compiler must support the "_rotl()" function generated inline to +; achieve maximal performance. +; +; It turns out the MSFT C compiler, coupled with newer processor, results +; in timings comparable to the 32-bit only phmd5a.asm hand-written assembly. +; Therefore this "C" implementation is now used. +; Avoiding assembly allows the code to be compiled either 32 or 64 bit. +; +; Note that a "little-endian" memory architecture is assumed. +; +; The Fx() and MD5STEP() macros were written by Colin Plumb in 1993. +; MD5STEP() was changed slightly to match how phmd5a.asm operates. +; +;---------------------------------------------------------------------------*/ + +#include // for _rotl() +#include "phmd5.h" + +// MD5 Optimisation Tricks by Anime Tosho +// https://github.com/animetosho/md5-optimisation#optimisation-tricks-single-buffer +// Dependency shortcut in G function +#define F1(x, y, z) ((x & y) + (~x & z)) + +//#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +#define MD5STEP(f, w, x, y, z, ix, s, sc) \ + w = _rotl(w + f(x, y, z) + ((unsigned *) pdata)[ix] + sc, s) + x + +void Phmd5DoBlocks( + unsigned char *hash, + char *pdata, + size_t bytecnt +) { + unsigned __int32 a = *(unsigned __int32 *) &hash[ 0]; + unsigned __int32 b = *(unsigned __int32 *) &hash[ 4]; + unsigned __int32 c = *(unsigned __int32 *) &hash[ 8]; + unsigned __int32 d = *(unsigned __int32 *) &hash[12]; + + do { + MD5STEP(F1, a, b, c, d, 0, 7, 0xd76aa478); + MD5STEP(F1, d, a, b, c, 1, 12, 0xe8c7b756); + MD5STEP(F1, c, d, a, b, 2, 17, 0x242070db); + MD5STEP(F1, b, c, d, a, 3, 22, 0xc1bdceee); + MD5STEP(F1, a, b, c, d, 4, 7, 0xf57c0faf); + MD5STEP(F1, d, a, b, c, 5, 12, 0x4787c62a); + MD5STEP(F1, c, d, a, b, 6, 17, 0xa8304613); + MD5STEP(F1, b, c, d, a, 7, 22, 0xfd469501); + MD5STEP(F1, a, b, c, d, 8, 7, 0x698098d8); + MD5STEP(F1, d, a, b, c, 9, 12, 0x8b44f7af); + MD5STEP(F1, c, d, a, b, 10, 17, 0xffff5bb1); + MD5STEP(F1, b, c, d, a, 11, 22, 0x895cd7be); + MD5STEP(F1, a, b, c, d, 12, 7, 0x6b901122); + MD5STEP(F1, d, a, b, c, 13, 12, 0xfd987193); + MD5STEP(F1, c, d, a, b, 14, 17, 0xa679438e); + MD5STEP(F1, b, c, d, a, 15, 22, 0x49b40821); + + MD5STEP(F2, a, b, c, d, 1, 5, 0xf61e2562); + MD5STEP(F2, d, a, b, c, 6, 9, 0xc040b340); + MD5STEP(F2, c, d, a, b, 11, 14, 0x265e5a51); + MD5STEP(F2, b, c, d, a, 0, 20, 0xe9b6c7aa); + MD5STEP(F2, a, b, c, d, 5, 5, 0xd62f105d); + MD5STEP(F2, d, a, b, c, 10, 9, 0x02441453); + MD5STEP(F2, c, d, a, b, 15, 14, 0xd8a1e681); + MD5STEP(F2, b, c, d, a, 4, 20, 0xe7d3fbc8); + MD5STEP(F2, a, b, c, d, 9, 5, 0x21e1cde6); + MD5STEP(F2, d, a, b, c, 14, 9, 0xc33707d6); + MD5STEP(F2, c, d, a, b, 3, 14, 0xf4d50d87); + MD5STEP(F2, b, c, d, a, 8, 20, 0x455a14ed); + MD5STEP(F2, a, b, c, d, 13, 5, 0xa9e3e905); + MD5STEP(F2, d, a, b, c, 2, 9, 0xfcefa3f8); + MD5STEP(F2, c, d, a, b, 7, 14, 0x676f02d9); + MD5STEP(F2, b, c, d, a, 12, 20, 0x8d2a4c8a); + + MD5STEP(F3, a, b, c, d, 5, 4, 0xfffa3942); + MD5STEP(F3, d, a, b, c, 8, 11, 0x8771f681); + MD5STEP(F3, c, d, a, b, 11, 16, 0x6d9d6122); + MD5STEP(F3, b, c, d, a, 14, 23, 0xfde5380c); + MD5STEP(F3, a, b, c, d, 1, 4, 0xa4beea44); + MD5STEP(F3, d, a, b, c, 4, 11, 0x4bdecfa9); + MD5STEP(F3, c, d, a, b, 7, 16, 0xf6bb4b60); + MD5STEP(F3, b, c, d, a, 10, 23, 0xbebfbc70); + MD5STEP(F3, a, b, c, d, 13, 4, 0x289b7ec6); + MD5STEP(F3, d, a, b, c, 0, 11, 0xeaa127fa); + MD5STEP(F3, c, d, a, b, 3, 16, 0xd4ef3085); + MD5STEP(F3, b, c, d, a, 6, 23, 0x04881d05); + MD5STEP(F3, a, b, c, d, 9, 4, 0xd9d4d039); + MD5STEP(F3, d, a, b, c, 12, 11, 0xe6db99e5); + MD5STEP(F3, c, d, a, b, 15, 16, 0x1fa27cf8); + MD5STEP(F3, b, c, d, a, 2, 23, 0xc4ac5665); + + MD5STEP(F4, a, b, c, d, 0, 6, 0xf4292244); + MD5STEP(F4, d, a, b, c, 7, 10, 0x432aff97); + MD5STEP(F4, c, d, a, b, 14, 15, 0xab9423a7); + MD5STEP(F4, b, c, d, a, 5, 21, 0xfc93a039); + MD5STEP(F4, a, b, c, d, 12, 6, 0x655b59c3); + MD5STEP(F4, d, a, b, c, 3, 10, 0x8f0ccc92); + MD5STEP(F4, c, d, a, b, 10, 15, 0xffeff47d); + MD5STEP(F4, b, c, d, a, 1, 21, 0x85845dd1); + MD5STEP(F4, a, b, c, d, 8, 6, 0x6fa87e4f); + MD5STEP(F4, d, a, b, c, 15, 10, 0xfe2ce6e0); + MD5STEP(F4, c, d, a, b, 6, 15, 0xa3014314); + MD5STEP(F4, b, c, d, a, 13, 21, 0x4e0811a1); + MD5STEP(F4, a, b, c, d, 4, 6, 0xf7537e82); + MD5STEP(F4, d, a, b, c, 11, 10, 0xbd3af235); + MD5STEP(F4, c, d, a, b, 2, 15, 0x2ad7d2bb); + MD5STEP(F4, b, c, d, a, 9, 21, 0xeb86d391); + + a += *(unsigned __int32 *) &hash[ 0]; + b += *(unsigned __int32 *) &hash[ 4]; + c += *(unsigned __int32 *) &hash[ 8]; + d += *(unsigned __int32 *) &hash[12]; + + *(unsigned __int32 *) &hash[ 0] = a; + *(unsigned __int32 *) &hash[ 4] = b; + *(unsigned __int32 *) &hash[ 8] = c; + *(unsigned __int32 *) &hash[12] = d; + + pdata += 64; + } while (bytecnt -= 64); +} diff --git a/source/par1j/res_par1j.rc b/source/par1j/res_par1j.rc new file mode 100644 index 0000000000000000000000000000000000000000..38ff85122c7923652b4c32ccd5c7f7d1f3f3be76 GIT binary patch literal 862 zcma))OH0E*6ot>Wg8w0OrBFf}yU?X=Er_KFH7&RdRbzalp$}BVzpj2WA=Rl?CCrfF zo_o%@kN3A-?WiSB6P;+JbDb*Gkb9^;e36N<~Cp3iO0H7go6^(vauI?cW@S>c1ElY%TjY?%9Y|e)4i_np6gmOqNbKI zt0p(k=rwg@bvxW8wzfvssO1=?vGq`2bNAq1XlgYJv@>*avK!V-M9#?ec8Ze>Y!0U4 z-sBt*PnCe|!cyqjvZlJRIVI!wh^Ia9`{)Ju4w#2WuU6zFy5o5CB+(=1lCw=aLTd(M zMJ-DvpXdQMRUHNIId$A_&98brA!|-Y+&;AA