Files
MultiPar/source/par2j/search.c
2024-11-30 13:06:17 +09:00

1244 lines
47 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// search.c
// Copyright : 2024-11-30 Yutaka Sawada
// License : GPL
#ifndef _UNICODE
#define _UNICODE
#endif
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0601 // Windows 7 or later
#endif
#include <stdio.h>
#include <windows.h>
#include "common2.h"
#include "crc.h"
#include "md5_crc.h"
#include "ini.h"
#include "json.h"
#include "search.h"
#define MIN_PACKET_SIZE 68
// ファイル情報のファイル名の文字コードを変換しなおす
static int convert_filename(unsigned int new_CP, unsigned int old_CP, file_ctx_r *files)
{
char ascii_buf[MAX_LEN * 3];
wchar_t uni_buf[MAX_LEN];
int i, j, len;
// 全てのファイル名を変換可能かを先に確かめる
for (i = 0; i < file_num; i++){
if (files[i].name <= 0)
continue; // ファイル名が取得できなかったファイルはとばす
// old_CP で変換されたユニコードから元のデータに戻す
if (utf16_to_cp(list_buf + files[i].name, ascii_buf, old_CP))
return 1;
// new_CP で再度変換しなおす
if (cp_to_utf16(ascii_buf, uni_buf, new_CP))
return 1;
}
// 先に確認済みなので、変換エラーは発生しないはず
for (i = 0; i < file_num; i++){
if (files[i].name <= 0)
continue; // ファイル名が取得できなかったファイルはとばす
// old_CP で変換されたユニコードから元のデータに戻す
utf16_to_cp(list_buf + files[i].name, ascii_buf, old_CP);
// new_CP で再度変換しなおす
cp_to_utf16(ascii_buf, uni_buf, new_CP);
// 変換前のファイル名をリストから取り除く
len = (int)wcslen(list_buf + files[i].name);
list_len = remove_file_path(list_buf, list_len, files[i].name);
for (j = 0; j < file_num; j++){
if (files[j].name > files[i].name)
files[j].name -= (len + 1); // リスト上で後ろのやつをずらしていく
}
// 変換しなおしたファイル名を追加する
files[i].name = list_len;
if (add_file_path(uni_buf))
return 2;
}
return 0;
}
// UTF-8 で記録しない PAR2 client かどうか
static int non_utf8_client(char *creator)
{
int version;
if (strncmp(creator, "QuickPar ", 9) == 0){
// QuickPar 0.9
version = creator[9] - 48;
version *= 10;
version += creator[10] - 48;
if (version <= 9)
return 1;
} else if (strncmp(creator, "Created by phpar2 version ", 26) == 0){
// Created by phpar2 version 1.3.
version = creator[26] - 48;
version *= 10;
version += creator[28] - 48;
if (version <= 13)
return 1;
}
return 0;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// リカバリ・ファイルを検索してファイル・リストに追加する
int search_recovery_files(void)
{
wchar_t recovery_base[MAX_LEN], search_path[MAX_LEN], file_ext[EXT_LEN];
wchar_t *tmp_p;
int len, dir_len, ext_len, l_max;
HANDLE hFind;
WIN32_FIND_DATA FindData;
get_base_dir(recovery_file, recovery_base);
dir_len = (int)wcslen(recovery_base);
// リカバリ・ファイルの名前の基
get_base_filename(recovery_file, search_path, file_ext);
if (switch_v & 2){ // 追加検査なら同じディレクトリ内の全ファイル
wcscpy(search_path, recovery_base);
wcscat(search_path, L"*");
if (file_ext[0] != 0) // 拡張子があったなら「*.拡張子」で検索する
wcscat(search_path, file_ext);
} else {
if (file_ext[0] != 0){ // 拡張子があったなら「.*拡張子」で検索する
file_ext[0] = '*';
wcscat(search_path, L".");
wcscat(search_path, file_ext);
file_ext[0] = '.';
} else {
wcscat(search_path, L"*");
}
}
ext_len = (int)wcslen(file_ext);
l_max = ALLOC_LEN;
recv_len = 0;
recv_buf = (wchar_t *)malloc(l_max * 2);
if (recv_buf == NULL){
printf("malloc, %d\n", l_max * 2);
return 1;
}
// リカバリ・ファイルを検索する
hFind = FindFirstFile(search_path, &FindData);
if (hFind == INVALID_HANDLE_VALUE){
print_win32_err();
printf_cp("cannot find recovery file, %s\n", search_path);
return 1;
}
do {
if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0){
len = (int)wcslen(FindData.cFileName);
if (dir_len + len >= MAX_LEN)
continue; // 長すぎるファイル名は無視する
if (file_ext[0] != 0){ // 拡張子が同じファイルだけ検査する
if ((len <= ext_len) || (_wcsicmp(FindData.cFileName + (len - ext_len), file_ext) != 0))
continue;
}
if (recv_len + dir_len + len >= l_max){ // 領域が足りなくなるなら拡張する
l_max += ALLOC_LEN;
tmp_p = (wchar_t *)realloc(recv_buf, l_max * 2);
if (tmp_p == NULL){
FindClose(hFind);
printf("realloc, %d\n", l_max * 2);
return 1;
} else {
recv_buf = tmp_p;
}
}
// リストにコピーする
wcscpy(recv_buf + recv_len, recovery_base);
recv_len += dir_len;
wcscpy(recv_buf + recv_len, FindData.cFileName);
recv_len += len + 1;
recovery_num++;
}
} while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する
FindClose(hFind);
// リカバリ・ファイルの拡張子が .par2 以外なら再検索する
if ((file_ext[0] != 0) && (_wcsicmp(file_ext, L".par2") != 0)){
get_base_filename(recovery_file, search_path, file_ext);
wcscat(search_path, L".");
wcscat(search_path, L"*");
wcscat(search_path, L".par2"); // 「.*.par2」で検索する
hFind = FindFirstFile(search_path, &FindData);
if (hFind != INVALID_HANDLE_VALUE){
do {
if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0){
len = (int)wcslen(FindData.cFileName);
if (dir_len + len >= MAX_LEN)
continue; // 長すぎるファイル名は無視する
if (recv_len + dir_len + len >= l_max){ // 領域が足りなくなるなら拡張する
l_max += ALLOC_LEN;
tmp_p = (wchar_t *)realloc(recv_buf, l_max * 2);
if (tmp_p == NULL){
FindClose(hFind);
printf("realloc, %d\n", l_max * 2);
return 1;
} else {
recv_buf = tmp_p;
}
}
// リストにコピーする
wcscpy(recv_buf + recv_len, recovery_base);
recv_len += dir_len;
wcscpy(recv_buf + recv_len, FindData.cFileName);
recv_len += len + 1;
recovery_num++;
}
} while (FindNextFile(hFind, &FindData)); // 次のファイルを検索する
FindClose(hFind);
}
}
if (recovery_num > 1)
sort_list(recv_buf, recv_len); // リスト内でファイル名順に並び替える (外部ファイルは並び替えない)
// 指定された検査対象にリカバリ・ファイルが含まれるかもしれない
if (list2_buf){
int list2_off = 0;
if (file_ext[0] != 0){ // リカバリ・ファイルの拡張子
ext_len = (int)wcslen(file_ext);
} else {
ext_len = MAX_LEN; // 拡張子が省略されてる場合は比較しない
}
while (list2_off < list2_len){
len = (int)wcslen(list2_buf + list2_off);
// ファイル名が重複しないようにする
if (search_file_path(recv_buf, recv_len, list2_buf + list2_off)){
list2_off += len + 1;
continue;
}
// 拡張子が「.par2」あるいはリカバリ・ファイルと同じ外部ファイルだけ検査する
if (((len > 5) && (_wcsicmp(list2_buf + (list2_off + len - 5), L".par2") == 0)) ||
((len > ext_len) && (_wcsicmp(list2_buf + (list2_off + len - ext_len), file_ext) == 0))){
if (recv_len + len >= l_max){ // 領域が足りなくなるなら拡張する
l_max += ALLOC_LEN;
tmp_p = (wchar_t *)realloc(recv_buf, l_max * 2);
if (tmp_p == NULL){
printf("realloc, %d\n", l_max * 2);
return 1;
} else {
recv_buf = tmp_p;
}
}
// リストにコピーする
wcscpy(recv_buf + recv_len, list2_buf + list2_off);
recv_len += len + 1;
recovery_num++;
}
list2_off += len + 1; // 次のファイルへ
}
}
// 探査時に除外するリストの領域を確保しておく
recv2_len = 0;
recv2_buf = (wchar_t *)malloc(recv_len * 2);
if (recv2_buf == NULL){
printf("malloc, %d\n", recv_len * 2);
return 1;
}
/*{
FILE *fp;
fp = fopen("par_list.txt", "wb");
fwrite(recv_buf, 2, recv_len, fp);
fclose(fp);
}*/
return 0;
}
// Magic sequence と一致するか (match = 0, fail = 1)
static int match_magic(unsigned char *buf)
{
if ((*buf++) != 'P')
return 1;
if ((*buf++) != 'A')
return 1;
if ((*buf++) != 'R')
return 1;
if ((*buf++) != '2')
return 1;
if ((*buf++) != 0)
return 1;
if ((*buf++) != 'P')
return 1;
if ((*buf++) != 'K')
return 1;
if ((*buf++) != 'T')
return 1;
return 0;
}
// Main packet を末尾から遡って探す
int search_main_packet(
unsigned char *buf, // 作業バッファー、File ID が戻る
unsigned char *set_id) // Recovery Set ID が戻る
{
unsigned char hash[16];
int num, recv_off, len, off;
unsigned int rv, packet_size, time_last;
__int64 file_size, file_off, end_size;
HANDLE hFile;
// リカバリ・ファイルの一覧を表示する
printf("PAR File list :\n");
printf(" Size : Filename\n");
total_file_size = 0;
recv_off = 0;
while (recv_off < recv_len){
// ファイルを開くことができるか、サイズを取得できるかを確かめる
hFile = CreateFile(recv_buf + recv_off, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE){
recv_len = remove_file_path(recv_buf, recv_len, recv_off); // リストから取り除く
recovery_num--;
continue;
}
// ファイルのサイズを取得する
if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){
CloseHandle(hFile);
recv_len = remove_file_path(recv_buf, recv_len, recv_off); // リストから取り除く
recovery_num--;
continue;
}
CloseHandle(hFile);
total_file_size += file_size;
// リカバリ・ファイルの名前
if (compare_directory(recovery_file, recv_buf + recv_off) == 0){ // 同じ場所なら
utf16_to_cp(offset_file_name(recv_buf + recv_off), buf, cp_output); // ファイル名だけにする
} else {
path_to_cp(recv_buf + recv_off, buf, cp_output); // パスも表示する
}
printf("%13I64d : \"%s\"\n", file_size, buf);
while (recv_buf[recv_off] != 0) // 次のファイルの位置にずらす
recv_off++;
recv_off++;
}
printf("\nPAR File total size\t: %I64d\n", total_file_size);
printf("PAR File possible count\t: %d\n\n", recovery_num);
recv_off = 0; // 先頭に戻しておく
prog_last = -1;
end_size = 0;
for (num = 0; num < recovery_num; num++){
if (cancel_progress() != 0) // キャンセル処理
return 2;
// リカバリ・ファイルを開く
if (num == 0){ // まずは指定されたリカバリ・ファイルから探す
hFile = CreateFile(recovery_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
} else {
if (_wcsicmp(recv_buf + recv_off, recovery_file) == 0){ // 最初に検査したリカバリ・ファイルはとばす
while (recv_buf[recv_off] != 0)
recv_off++;
recv_off++;
}
hFile = CreateFile(recv_buf + recv_off, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
while (recv_buf[recv_off] != 0)
recv_off++;
recv_off++;
}
if (hFile == INVALID_HANDLE_VALUE)
continue; // エラーが発生したら次のリカバリ・ファイルを調べる
// ファイルのサイズを取得する
if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){
CloseHandle(hFile);
continue; // エラーが発生したら次のリカバリ・ファイルを調べる
}
if (file_size < MIN_PACKET_SIZE){
CloseHandle(hFile);
continue; // 小さすぎるファイルは検査しない
}
// 読み取りサイズは最大で SEARCH_SIZE
len = SEARCH_SIZE;
if (file_size < SEARCH_SIZE)
len = (unsigned int)file_size;
//printf("num = %d, file_size = %I64d\n", num, file_size);
time_last = GetTickCount();
// 末尾から遡って読み込んでいく
file_off = file_size;
while (len > 0){
file_off -= len; // 読み取り開始位置になる
// SEARCH_SIZE を読み込んでバッファーの先頭に入れる
if (file_read_data(hFile, file_off, buf, len)){
len = 0; // 読み込み時にエラーが発生したファイルはそれ以上検査しない
break;
}
// Main packet を探す
off = 0;
while (off < len){
if (match_magic(buf + off) == 0){
memcpy(&packet_size, buf + (off + 12), 4); // パケット・サイズの上位4バイトが0以外だとだめ
if (packet_size != 0){
off += 8;
continue;
}
memcpy(&packet_size, buf + (off + 8), 4); // パケット・サイズを確かめる
if ((packet_size & 3) || (packet_size < MIN_PACKET_SIZE) || (off + packet_size > SEARCH_SIZE * 3)){
off += 8;
continue;
}
// パケット全体がバッファー内にあれば破損してないかを調べる
data_md5(buf + (off + 32), (packet_size - 32), hash);
if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見
if (memcmp(buf + (off + 48), "PAR 2.0\0Main\0\0\0\0", 16) == 0){
// 念のためデータの整合性を検査する
rv = 0;
memcpy(&block_size, buf + (off + 68), 4); // Slice size の上位 4バイトを確かめる
if (block_size != 0) // ブロック・サイズが 4GB以上だとエラー
rv++;
memcpy(&block_size, buf + (off + 64), 4); // Slice size
if (block_size > MAX_BLOCK_SIZE) // 対応するブロック・サイズは 1GB まで
rv++;
if ((block_size & 3) != 0) // ブロック・サイズは 4 の倍数のはず
rv++;
memcpy(&entity_num, buf + (off + 72), 4); // Number of files in the recovery set.
file_num = (packet_size - 76) / 16;
if (file_num <= 0)
rv++;
if (entity_num > file_num)
rv++;
if (rv == 0){
memcpy(set_id, buf + (off + 32), 16); // Recovery Set ID
memmove(buf, buf + (off + 76), packet_size - 76); // File ID をバッファーの先頭に移す
CloseHandle(hFile);
// Recovery Set の情報を表示する
printf("Recovery Set ID\t\t: ");
print_hash(set_id);
printf("\n");
printf("Input File Slice size\t: %u\n", block_size);
printf("Input File total count\t: %d\n", file_num);
printf("Recovery Set file count : %d\n", entity_num);
return 0;
}
}
off += packet_size; // パケットの終端まで一気に飛ぶ
} else {
off += 8;
}
} else {
off++;
}
}
// 簡易検査なら検査領域を制限する(ファイルの後半分だけ)
if (((switch_v & 1) != 0) && (file_off < file_size / 2)){ // 最低でも後半分は検査する
len = 0;
break;
}
// バッファーの前半 SEARCH_SIZE * 2 を次の読み込みサイズ分だけ後ろにずらす
len = SEARCH_SIZE;
if (file_off < SEARCH_SIZE)
len = (unsigned int)file_off;
if (len > 0)
memmove(buf + len, buf, SEARCH_SIZE * 2);
// 経過表示
if (GetTickCount() - time_last >= UPDATE_TIME){
if (print_progress((int)(((end_size + file_size - file_off) * 1000) / total_file_size))){
CloseHandle(hFile);
return 2;
}
time_last = GetTickCount();
}
}
CloseHandle(hFile);
end_size += file_size;
}
if ((prog_last >= 0) && (prog_last != 1000))
printf("100.0%%\r");
return 1;
}
// ファイル情報のパケットを探す
int search_file_packet(
char *ascii_buf,
unsigned char *buf, // 作業バッファー
wchar_t *par_comment, // Unicode コメントを入れる
unsigned char *set_id, // Recovery Set ID を確かめる
int flag_sanitize, // 0以外 = ファイル名を浄化する
file_ctx_r *files) // 各ソース・ファイルの情報
{
char par_client[COMMENT_LEN], ascii_comment[COMMENT_LEN];
unsigned char hash[16];
wchar_t uni_buf[MAX_LEN];
int i, j, k, recv_off, find_num;
int len, off, max, used_CP = CP_UTF8;
unsigned int packet_size, time_last;
__int64 file_size, file_off, file_next, end_size;
HANDLE hFile;
par_client[0] = 0;
par_comment[0] = 0;
ascii_comment[0] = 0;
list_len = 1; // 先頭に不明用の null 文字を置く
list_max = ALLOC_LEN;
list_buf = (wchar_t *)malloc(list_max * 2);
if (list_buf == NULL){
printf("malloc, %d\n", list_max * 2);
return 1;
}
list_buf[0] = 0;
find_num = 0;
recv_off = 0;
end_size = 0;
while (recv_off < recv_len){
if (cancel_progress() != 0) // キャンセル処理
return 2;
//utf16_to_cp(recv_buf + recv_off, ascii_buf, cp_output);
//printf("verifying %s\n", ascii_buf);
// リカバリ・ファイルを開く
hFile = CreateFile(recv_buf + recv_off, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
while (recv_buf[recv_off] != 0) // 次のファイルの位置にずらす
recv_off++;
recv_off++;
if (hFile == INVALID_HANDLE_VALUE)
continue; // エラーが発生したら次のリカバリ・ファイルを調べる
// ファイルのサイズを取得する
if (!GetFileSizeEx(hFile, (PLARGE_INTEGER)&file_size)){
CloseHandle(hFile);
continue; // エラーが発生したら次のリカバリ・ファイルを調べる
}
if (file_size < MIN_PACKET_SIZE){
CloseHandle(hFile);
continue; // 小さすぎるファイルは検査しない
}
file_off = 0;
time_last = GetTickCount();
// 最初はファイルの先頭 SEARCH_SIZE * 3 を読み込む
off = 0;
len = SEARCH_SIZE * 3;
if (file_size < SEARCH_SIZE * 3)
len = (unsigned int)file_size;
// 最大 SEARCH_SIZE * 3 を読み込んでバッファーの先頭に入れる
if (file_read_data(hFile, 0, buf, len)){
CloseHandle(hFile);
continue; // エラーが発生したら次のリカバリ・ファイルを調べる
}
file_next = len; // 次の読み込み開始位置
max = len;
if (len > SEARCH_SIZE)
max = SEARCH_SIZE;
// パケットを探す
while (len > 0){
while (off < max){
if (match_magic(buf + off) == 0){
memcpy(&packet_size, buf + (off + 12), 4); // パケット・サイズの上位4バイトが0以外だとだめ
if (packet_size != 0){
off += 8;
continue;
}
memcpy(&packet_size, buf + (off + 8), 4); // パケット・サイズを確かめる
if ((packet_size & 3) || (packet_size < MIN_PACKET_SIZE) || (file_off + (__int64)off + (__int64)packet_size > file_size)){
off += 8;
continue;
}
if (off + packet_size > SEARCH_SIZE * 3){ // 大きすぎてバッファー内に収まってないなら
if (packet_size > 64 + 4 + block_size){ // Recovery Slice packet よりも大きいパケットは無いはず
off += 8;
continue;
}
if ((switch_v & 1) == 0){ // 簡易検査でなければ
// パケットが破損してないかを調べる
if (file_md5(hFile, file_off + off + 32, packet_size - 32, hash)){
off += 8; // 計算できなかったら
} else {
if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見
// バッファー内のオフセットとパケット・サイズ分ずらす
if (file_next < file_size)
file_next = file_off + off + packet_size;
off = max; // 一気に末尾までいく
} else {
off += 8; // 破損パケットなら
}
}
} else { // 簡易検査では巨大パケットが完全かは調べない
// バッファー内のオフセットとパケット・サイズ分ずらす
if (file_next < file_size)
file_next = file_off + off + packet_size;
off = max; // 一気に末尾までいく
}
continue;
}
// パケットが破損してないかを調べる
data_md5(buf + (off + 32), (packet_size - 32), hash);
if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見
if (memcmp(buf + (off + 32), set_id, 16) != 0){ // Recovery Set ID が同じパケットだけ
off += packet_size;
continue;
}
/*
memset(ascii_buf, 0, 9);
memcpy(ascii_buf, buf + (off + 56), 8);
printf(" packet : %s, size = %u, file_off = %I64d, off = %d\n", ascii_buf, packet_size, file_off, off);
*/
if ((memcmp(buf + (off + 48), "PAR 2.0\0FileDesc", 16) == 0) && (packet_size - 120 < MAX_LEN * 3)){
// File Description packet
memcpy(ascii_buf, buf + (off + 120), packet_size - 120);
ascii_buf[packet_size - 120] = 0; // 末尾を null 文字にしておく
j = -1; // File ID が重複してないか確認する
for (i = 0; i < file_num; i++){
if (memcmp(buf + (off + 64), files[i].id, 16) == 0){ // File ID でどのファイルか探す
if (files[i].name < 0){ // 新しく発見したのなら
break;
} else { // 同じ情報が既に在った場合
j = i;
}
}
}
if (i < file_num){
memcpy(files[i].hash, buf + (off + 80), 32);
memcpy(&(files[i].size), buf + (off + 112), 8);
if ((j >= 0) && (i != j)){ // File ID が同じファイルが他に存在する
// ファイル内容が異なるのに同じ File ID になると区別できないので駄目!
// 逆に、ファイル内容 (ハッシュ値とサイズ) が同じなら問題ない?
if ((files[i].size != files[j].size) ||
(memcmp(files[i].hash, files[j].hash, 32) != 0)){
printf("same File ID, %d and %d\n", j, i);
return 1;
}
}
// パケット内に記録されてるファイル名をユニコードに変換する
if (used_CP != CP_UTF8){ // QuickPar などファイル名が UTF-8 以外なら
// 変換できるか調べる
j = cp_to_utf16(ascii_buf, uni_buf, used_CP);
if (j == ERROR_NO_UNICODE_TRANSLATION){ // CP1252 では変換エラーは発生しない
j = cp_to_utf16(ascii_buf, uni_buf, 1252);
if (j == 0){
k = convert_filename(1252, used_CP, files); // これまでのファイル名を変換しなおす
if (k != 0){
printf("convert_filename\n");
return 1;
}
}
used_CP = 1252; // これ以降のファイル名は Latin-1 CP1252 とみなす
}
} else if (check_utf8(ascii_buf) != 0){ // UTF-8 以外の文字をみつけたら
used_CP = CP_ACP; // これ以降のファイル名は全て UTF-8 以外とみなす
// ACP で変換できるか調べる
j = cp_to_utf16(ascii_buf, uni_buf, used_CP);
if (j == ERROR_NO_UNICODE_TRANSLATION){ // CP1252 では変換エラーは発生しない
j = cp_to_utf16(ascii_buf, uni_buf, 1252);
used_CP = 1252; // これ以降のファイル名は Latin-1 CP1252 とみなす
}
if (j == 0){
k = convert_filename(used_CP, CP_UTF8, files); // これまでのファイル名を変換しなおす
if (k == 1){ // CP1252 では変換エラーは発生しない
j = cp_to_utf16(ascii_buf, uni_buf, 1252);
k = convert_filename(1252, CP_UTF8, files);
used_CP = 1252; // これ以降のファイル名は Latin-1 CP1252 とみなす
}
if (k != 0){
printf("convert_filename\n");
return 1;
}
}
} else {
utf8_to_utf16(ascii_buf, uni_buf);
j = 0;
}
if (j == 0){
files[i].name = list_len;
if (add_file_path(uni_buf)){
printf("add_file_path\n");
return 1;
}
} else {
files[i].name = 0; // パケットは存在するけどファイル名の変換に失敗した
}
find_num++; // 完全な File Description packet の数
}
} else if ((par_client[0] == 0) && (memcmp(buf + (off + 48), "PAR 2.0\0Creator\0", 16) == 0)){
// Creater packet
i = packet_size - 64; // Creater のバイト数
if (i >= COMMENT_LEN)
i = COMMENT_LEN - 1;
memcpy(par_client, buf + (off + 64), i);
par_client[i] = 0; // 末尾を null 文字にする
} else if ((par_comment[0] == 0) && (ascii_comment[0] == 0) && (memcmp(buf + (off + 48), "PAR 2.0\0CommASCI", 16) == 0)){
// ASCII Comment packet
i = packet_size - 64; // コメントのバイト数
if (i >= COMMENT_LEN)
i = COMMENT_LEN - 1;
memcpy(ascii_comment, buf + (off + 64), i);
ascii_comment[i] = 0; // 末尾を null 文字にする
} else if ((par_comment[0] == 0) && (memcmp(buf + (off + 48), "PAR 2.0\0CommUni\0", 16) == 0)){
// Unicode Comment packet
i = packet_size - 80; // コメントのバイト数
if (i >= COMMENT_LEN * 2)
i = COMMENT_LEN * 2 - 2;
memcpy(par_comment, buf + (off + 80), i);
par_comment[i / 2] = 0; // 末尾を null 文字にする
} else if ((memcmp(buf + (off + 48), "PAR 2.0\0UniFileN", 16) == 0) && (packet_size - 80 < MAX_LEN * 2)){
// Unicode Filename packet
memset(uni_buf, 0, sizeof(uni_buf));
memcpy(uni_buf, buf + (off + 80), packet_size - 80);
for (i = 0; i < file_num; i++){
if (memcmp(buf + (off + 64), files[i].id, 16) == 0) // File ID が一致すれば
break;
}
if (i < file_num){ // File Description packet を先に読み込んでる状態でしか認識しない
if (files[i].name == 0){ // パケットは存在するけどファイル名の変換に失敗してる場合
// 新しいファイル名
files[i].name = list_len;
if (add_file_path(uni_buf)){
printf("add_file_path\n");
return 1;
}
} else if ((files[i].name > 0) && (wcscmp(uni_buf, list_buf + files[i].name) != 0)){
// パケットが存在してファイル名も取得してるけどユニコード版とは異なる場合
k = (int)wcslen(list_buf + files[i].name);
list_len = remove_file_path(list_buf, list_len, files[i].name);
for (j = 0; j < file_num; j++){
if (files[j].name > files[i].name)
files[j].name -= (k + 1); // リスト上で後ろのやつをずらしていく
}
// 新しいファイル名
files[i].name = list_len;
if (add_file_path(uni_buf)){
printf("add_file_path\n");
return 1;
}
}
}
}
off += packet_size; // パケットの終端まで一気に飛ぶ
} else {
off += 8;
}
} else {
off++;
}
}
// 全てのファイル情報を取得してしまえば抜ける
if ((find_num >= file_num) && (par_client[0] != 0))
break;
if (file_next < file_size){ // ファイル・データがまだ残ってれば
if (file_next > file_off + SEARCH_SIZE * 3){ // 次の読み込み位置が前回よりも SEARCH_SIZE * 3 以上離れてるなら
file_off = file_next;
// 読み取りサイズは最大で SEARCH_SIZE * 3
off = 0;
len = SEARCH_SIZE * 3;
if (file_size < file_next + SEARCH_SIZE * 3)
len = (unsigned int)(file_size - file_next);
// 最大 SEARCH_SIZE * 3 を読み込んでバッファーの先頭に入れる
if (file_read_data(hFile, file_next, buf, len)){
off = len; // 読み込み時にエラーが発生した部分は検査しない
file_next = file_size; // 次は読み込まず、このファイルの検査を終える
}
file_next += len; // 次の読み込み開始位置
max = len;
if (len > SEARCH_SIZE)
max = SEARCH_SIZE;
} else { // 次の SEARCH_SIZE を読み込む
// バッファーの内容を前にずらす
memcpy(buf, buf + SEARCH_SIZE, SEARCH_SIZE);
memcpy(buf + SEARCH_SIZE, buf + SEARCH_SIZE * 2, SEARCH_SIZE);
file_off += SEARCH_SIZE;
// 読み取りサイズは最大で SEARCH_SIZE
off -= max;
len = SEARCH_SIZE;
if (file_size < file_next + SEARCH_SIZE)
len = (unsigned int)(file_size - file_next);
// 最大 SEARCH_SIZE を読み込んでバッファーの末尾に入れる
if (file_read_data(hFile, file_next, buf + SEARCH_SIZE * 2, len)){
memset(buf + SEARCH_SIZE * 2, 0, len); // 読み込み時にエラーが発生した部分は 0 にしておく
file_next = file_size; // 次は読み込まず、このファイルの検査を終える
}
file_next += len; // 次の読み込み開始位置
max = SEARCH_SIZE;
len += SEARCH_SIZE * 2; // 未処理データのサイズに残ってる SEARCH_SIZE * 2 を足す
}
} else { // バッファー内の残りデータを処理する
off -= max;
len -= max;
if (len > 0){
if (len > SEARCH_SIZE){
max = SEARCH_SIZE;
memcpy(buf, buf + SEARCH_SIZE, SEARCH_SIZE);
memcpy(buf + SEARCH_SIZE, buf + SEARCH_SIZE * 2, len - SEARCH_SIZE);
} else {
max = len;
memcpy(buf, buf + SEARCH_SIZE, len); // バッファーの内容を前にずらす
}
file_off += SEARCH_SIZE;
}
}
// 経過表示
if (GetTickCount() - time_last >= UPDATE_TIME){
if (print_progress((int)(((end_size + file_off) * 1000) / total_file_size))){
CloseHandle(hFile);
return 2;
}
time_last = GetTickCount();
}
}
CloseHandle(hFile);
end_size += file_size;
// 全てのファイル情報を取得してしまえば抜ける
if ((find_num >= file_num) && (par_client[0] != 0))
break;
}
// QuickPar と phpar2 はファイル名を UTF-8 で記録しないことがわかってる
if ((used_CP == CP_UTF8) && (par_client[0] != 0) && (non_utf8_client(par_client) != 0)){
k = convert_filename(CP_ACP, CP_UTF8, files); // これまでのファイル名を変換しなおす
if (k == 1){
used_CP = 1252; // Latin-1 CP1252 で変換を試みる
k = convert_filename(1252, CP_UTF8, files);
}
if (k != 0){
printf("convert_filename\n");
return 1;
}
}
// 作成したクライアントとコメントを表示する
if (par_client[0] != 0){
for (i = 0; i < COMMENT_LEN; i++){ // 表示する前に sanitalize する
if (par_client[i] == 0){
break;
} else if ((par_client[i] <= 31) || (par_client[i] >= 127)){ // 制御文字を消す、非 ASCII 文字も
par_client[i] = ' ';
}
}
printf("Creator : %s\n", par_client);
}
if ((par_comment[0] == 0) && (ascii_comment[0] != 0)){ // ユニコードのコメントを優先する
if (!MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, ascii_comment, -1, par_comment, COMMENT_LEN))
par_comment[0] = 0;
}
if (par_comment[0] != 0){
for (i = 0; i < COMMENT_LEN; i++){ // 表示する前に sanitalize する
if (par_comment[i] == 0){
break;
} else if ((par_comment[i] <= 31) || (par_comment[i] == 127)){ // 制御文字を消す
par_comment[i] = ' ';
}
}
if (!utf16_to_cp(par_comment, ascii_buf, cp_output))
printf("Comment : %s\n", ascii_buf);
}
write_ini_file2(par_client, par_comment);
// リスト内のファイル名を検査して、問題があれば浄化する
// ついでにディレクトリ記号を「\」に統一する
if (flag_sanitize != 0){
max = 0;
for (i = 0; i < file_num; i++){
if (files[i].name <= 0)
continue; // ファイル名が取得できなかったファイルはとばす
wcscpy(uni_buf, list_buf + files[i].name);
off = sanitize_filename(uni_buf, files, i);
if (off != 0){
if ((off == 1) || (off == 9)){ // ディレクトリ記号を含んでる
wcscpy(list_buf + files[i].name, uni_buf);
off ^= 1;
}
if (off != 0){
if (max == 0){
max = 1;
printf("\nWarning about filenames :\n");
}
utf16_to_cp(uni_buf, ascii_buf, cp_output);
if (off == 16){
max++;
printf("file%d: \"%s\" is invalid\n", i, ascii_buf);
} else if (off == 8){
printf("file%d: \"%s\" is invalid\n", i, ascii_buf);
} else {
printf("file%d: \"%s\" was sanitized\n", i, ascii_buf);
if (off & 4){ // 文字数が変わった
k = (int)wcslen(list_buf + files[i].name);
list_len = remove_file_path(list_buf, list_len, files[i].name);
for (j = 0; j < file_num; j++){
if (files[j].name > files[i].name)
files[j].name -= (k + 1); // リスト上で後ろのやつをずらしていく
}
// 新しいファイル名
files[i].name = list_len;
if (add_file_path(uni_buf)){
printf("add_file_path\n");
return 1;
}
} else { // 文字数が同じなら元の場所にコピーする
wcscpy(list_buf + files[i].name, uni_buf);
}
}
}
}
}
if (max > 1)
return 1;
}
return 0;
}
// 修復用のパケットを探す
// 0~= 不完全なリカバリ・ファイルの数を返す、-2=Cansel
int search_recovery_packet(
char *ascii_buf,
unsigned char *buf, // 作業バッファー
wchar_t *file_path,
unsigned char *set_id, // Recovery Set ID を確かめる
HANDLE *rcv_hFile, // (verify NULL)
file_ctx_r *files, // 各ソース・ファイルの情報
source_ctx_r *s_blk, // 各ソース・ブロックの情報
parity_ctx_r *p_blk) // 各パリティ・ブロックの情報
{
unsigned char hash[16];
int i, j, recv_off, num, packet_count, bad_flag, file_block;
int find_num, find_new, recovery_lost;
int len, off, max;
unsigned int packet_size, time_last, meta_data[7];
__int64 file_size, file_off, file_next;
HANDLE hFile;
printf("\nLoading PAR File :\n");
printf(" Packet Slice Status : Filename\n");
fflush(stdout);
count_last = 0;
recovery_lost = 0; // 不完全なリカバリ・ファイルの数
first_num = 0; // 初めて見つけたパリティ・ブロックの数
num = 0;
recv_off = 0;
while (recv_off < recv_len){
if (cancel_progress() != 0) // キャンセル処理
return -2;
//utf16_to_cp(recv_buf + recv_off, ascii_buf, cp_output);
//printf("verifying %s\n", ascii_buf);
// リカバリ・ファイルを開く
hFile = CreateFile(recv_buf + recv_off, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE){
i = -1;
} else {
// 検査するリカバリ・ファイルが同じであれば、再検査する必要は無い
i = check_ini_recovery(hFile, meta_data);
}
if (i < 0){ // エラーが発生したら次のリカバリ・ファイルを調べる
while (recv_buf[recv_off] != 0) // 次のファイルの位置にずらす
recv_off++;
recv_off++;
continue;
}
if (i == 0){
memcpy(&file_size, meta_data, 8);
find_num = 0;
find_new = 0;
file_off = 0;
prog_last = -1;
time_last = GetTickCount();
// 最初はファイルの先頭 SEARCH_SIZE * 3 を読み込む
bad_flag = 0;
off = 0;
len = SEARCH_SIZE * 3;
if (file_size < SEARCH_SIZE * 3)
len = (unsigned int)file_size;
if (file_size < MIN_PACKET_SIZE){ // 小さすぎるファイルは検査しない
bad_flag |= 1;
len = 0;
}
// 最大 SEARCH_SIZE * 3 を読み込んでバッファーの先頭に入れる
if (file_read_data(hFile, 0, buf, len)){
off = len; // 読み込み時にエラーが発生した部分は検査しない
bad_flag |= 128;
file_next = file_size; // 次は読み込まず、このファイルの検査を終える
} else {
file_next = len; // 次の読み込み開始位置
}
max = len;
if (len > SEARCH_SIZE)
max = SEARCH_SIZE;
//printf("file_size = %I64d, max = %d, off = %d, len = %d\n", file_size, max, off, len);
// パケットを探す
packet_count = 0;
while (len > 0){
while (off < max){
if (match_magic(buf + off) == 0){
memcpy(&packet_size, buf + (off + 12), 4); // パケット・サイズの上位4バイトが0以外だとだめ
if (packet_size != 0){
bad_flag |= 1;
off += 8;
continue;
}
memcpy(&packet_size, buf + (off + 8), 4); // パケット・サイズを確かめる
if ((packet_size & 3) || (packet_size < MIN_PACKET_SIZE) || (file_off + (__int64)off + (__int64)packet_size > file_size)){
bad_flag |= 1;
off += 8;
continue;
}
if (off + packet_size > SEARCH_SIZE * 3){ // 大きすぎてバッファー内に収まってないなら
if (packet_size > 64 + 4 + block_size){ // Recovery Slice packet よりも大きいパケットは無いはず
off += 8;
continue;
}
// パケットが破損してないかを調べる
if (file_md5(hFile, file_off + off + 32, packet_size - 32, hash)){
off += 8; // 計算できなかったら
} else {
if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見
if (memcmp(buf + (off + 32), set_id, 16) == 0){ // Recovery Set ID が同じなら
// 少なくともパケットの先頭 SEARCH_SIZE * 2 まではバッファー内に存在する
// バッファーに収まらないパケットは Recovery Slice packet だけのはず
if (memcmp(buf + (off + 48), "PAR 2.0\0RecvSlic", 16) == 0){
// Recovery Slice packet
memcpy(&j, buf + (off + 64), 4); // パリティ・ブロックの番号
j &= 0xFFFF; // 番号は 16-bit (65536以上は 065535 の繰り返し)
if (j == 65535) // (0 と 65535 は同じ)
j = 0;
if (j < parity_num){
if (p_blk[j].exist == 0){
find_new++; // リカバリ・ファイル内の新しいパリティ・ブロックの数
first_num++;
p_blk[j].exist = 1;
p_blk[j].file = num;
p_blk[j].off = file_off + off + 68;
}
write_ini_recovery(j, file_off + off + 68);
find_num++; // リカバリ・ファイル内のパリティ・ブロックの数
}
}
packet_count++;
} else {
bad_flag |= 2; // 別の Set ID が混じってる
}
// バッファー内のオフセットとパケット・サイズ分ずらす
if (file_next < file_size)
file_next = file_off + off + packet_size;
off = max; // 一気に末尾までいく
} else {
off += 8; // 破損パケットなら
}
}
continue;
}
// パケットが破損してないかを調べる
data_md5(buf + (off + 32), (packet_size - 32), hash);
if (memcmp(buf + (off + 16), hash, 16) == 0){ // 完全なパケット発見
if (memcmp(buf + (off + 32), set_id, 16) != 0){ // Recovery Set ID が同じパケットだけ
bad_flag |= 2; // 別の Set ID が混じってる
off += packet_size;
continue;
}
/*
memset(ascii_buf, 0, 9);
memcpy(ascii_buf, buf + (off + 56), 8);
printf(" packet : %s, size = %u, file_off = %I64d, off = %d\n", ascii_buf, packet_size, file_off, off);
*/
if (memcmp(buf + (off + 48), "PAR 2.0\0IFSC\0\0\0\0", 16) == 0){
// Input File Slice Checksum packet
for (j = 0; j < entity_num; j++){
if (memcmp(buf + (off + 64), files[j].id, 16) == 0){ // File ID が一致すれば
if (files[j].state & 0x80) // チェックサムを新しく発見したのなら
break;
}
}
file_block = files[j].b_num; // ソース・ブロックの数
if ((j < entity_num) && ((int)((packet_size - 80) / 20) == file_block)){
files[j].state = 0; // そのファイルのチェックサムを見つけた
for (i = 0; i < file_block; i++){
memcpy(s_blk[files[j].b_off + i].hash, buf + (off + 80 + (20 * i)), 20);
memcpy(&(s_blk[files[j].b_off + i].crc), buf + (off + 80 + (20 * i) + 16), 4);
s_blk[files[j].b_off + i].crc ^= window_mask; // CRC の初期値と最終処理の 0xFFFFFFFF を取り除く
}
}
} else if (memcmp(buf + (off + 48), "PAR 2.0\0RecvSlic", 16) == 0){
// Recovery Slice packet
memcpy(&j, buf + (off + 64), 4); // パリティ・ブロックの番号
j &= 0xFFFF; // 番号は 16-bit (65536以上は 065535 の繰り返し)
if (j == 65535) // (0 と 65535 は同じ)
j = 0;
if ((packet_size == 64 + 4 + block_size) && (j < parity_num)){
if (p_blk[j].exist == 0){
find_new++; // リカバリ・ファイル内の新しいパリティ・ブロックの数
first_num++;
p_blk[j].exist = 1;
p_blk[j].file = num;
p_blk[j].off = file_off + off + 68;
}
write_ini_recovery(j, file_off + off + 68);
find_num++; // リカバリ・ファイル内のパリティ・ブロックの数
}
}
packet_count++;
off += packet_size; // パケットの終端まで一気に飛ぶ
} else {
bad_flag |= 1;
off += 8;
}
} else {
bad_flag |= 1;
off++;
}
}
if (file_next < file_size){ // ファイル・データがまだ残ってれば
if (file_next > file_off + SEARCH_SIZE * 3){ // 次の読み込み位置が前回よりも SEARCH_SIZE * 3 以上離れてるなら
file_off = file_next;
// 読み取りサイズは最大で SEARCH_SIZE * 3
off = 0;
len = SEARCH_SIZE * 3;
if (file_size < file_next + SEARCH_SIZE * 3)
len = (unsigned int)(file_size - file_next);
// 最大 SEARCH_SIZE * 3 を読み込んでバッファーの先頭に入れる
if (file_read_data(hFile, file_next, buf, len)){
off = len; // 読み込み時にエラーが発生した部分は検査しない
bad_flag |= 128;
file_next = file_size; // 次は読み込まず、このファイルの検査を終える
}
file_next += len; // 次の読み込み開始位置
max = len;
if (len > SEARCH_SIZE)
max = SEARCH_SIZE;
} else { // 次の SEARCH_SIZE を読み込む
// バッファーの内容を前にずらす
memcpy(buf, buf + SEARCH_SIZE, SEARCH_SIZE);
memcpy(buf + SEARCH_SIZE, buf + SEARCH_SIZE * 2, SEARCH_SIZE);
file_off += SEARCH_SIZE;
// 読み取りサイズは最大で SEARCH_SIZE
off -= max;
len = SEARCH_SIZE;
if (file_size < file_next + SEARCH_SIZE)
len = (unsigned int)(file_size - file_next);
// 最大 SEARCH_SIZE を読み込んでバッファーの末尾 SEARCH_SIZE に入れる
if (file_read_data(hFile, file_next, buf + SEARCH_SIZE * 2, len)){
memset(buf + SEARCH_SIZE * 2, 0, len); // 読み込み時にエラーが発生した部分は 0 にしておく
bad_flag |= 128;
file_next = file_size; // 次は読み込まず、このファイルの検査を終える
}
file_next += len; // 次の読み込み開始位置
max = SEARCH_SIZE;
len += SEARCH_SIZE * 2; // 未処理データのサイズに残ってる SEARCH_SIZE * 2 を足す
}
} else { // バッファー内の残りデータを処理する
//printf("max = %d, off = %d, len = %d\n", max, off, len);
off -= max;
len -= max;
if (len > 0){
if (len > SEARCH_SIZE){
max = SEARCH_SIZE;
memcpy(buf, buf + SEARCH_SIZE, SEARCH_SIZE);
memcpy(buf + SEARCH_SIZE, buf + SEARCH_SIZE * 2, len - SEARCH_SIZE);
} else {
max = len;
memcpy(buf, buf + SEARCH_SIZE, len); // バッファーの内容を前にずらす
}
file_off += SEARCH_SIZE;
}
}
// 経過表示
if (GetTickCount() - time_last >= UPDATE_TIME){
if (print_progress_file((int)((file_off * 1000) / file_size), first_num, recv_buf + recv_off)){
CloseHandle(hFile);
write_ini_recovery2(-1, 0, 0, meta_data);
return -2;
}
time_last = GetTickCount();
}
}
write_ini_recovery2(packet_count, find_num, bad_flag, meta_data);
} else { // 検査済みなら記録を読み込む
find_new = read_ini_recovery(num, &packet_count, &find_num, &bad_flag, p_blk);
if (find_new < 0){
bad_flag = 1;
packet_count = 0;
find_num = 0;
} else {
first_num += find_new; // 新たに発見したパリティ・ブロックの数を合計する
}
}
print_progress_file(-1, first_num, NULL); // 重複を除外した合計ブロック数を表示する
if ((find_new > 0) && (rcv_hFile != NULL)){
rcv_hFile[num] = hFile; // 後から読み込めるように開いたままにする
} else {
CloseHandle(hFile);
}
// リカバリ・ファイルの名前
if (compare_directory(recovery_file, recv_buf + recv_off) == 0){ // 同じ場所なら
utf16_to_cp(offset_file_name(recv_buf + recv_off), ascii_buf, cp_output); // ファイル名だけにする
} else {
path_to_cp(recv_buf + recv_off, ascii_buf, cp_output); // パスも表示する
}
if (bad_flag == 0){
// 良好 (パケット単位なので完全に元と同じかは判別できない)
printf("%7d %5d Good : \"%s\"\n", packet_count, find_num, ascii_buf);
// 削除する時用に記録しておく
len = (int)wcslen(recv_buf + recv_off);
wcscpy(recv2_buf + recv2_len, recv_buf + recv_off);
recv2_len += len + 1;
// 次のファイルの位置にずらす
recv_off += len + 1;
} else if (packet_count == 0){
// パケットが全く見つからなかった (リカバリ・ファイルではない)
// 別の Set ID のリカバリ・ファイルも含む
printf(" 0 0 Useless : \"%s\"\n", ascii_buf);
recovery_lost++;
if (bad_flag == 2){ // 別の Set ID のリカバリ・ファイルで良好なら
// 次のファイルの位置にずらす
while (recv_buf[recv_off] != 0)
recv_off++;
recv_off++;
} else { // リカバリ・ファイルでなければリストから取り除く (ファイル数はそのまま)
recv_len = remove_file_path(recv_buf, recv_len, recv_off);
}
} else {
printf("%7d %5d Damaged : \"%s\"\n", packet_count, find_num, ascii_buf);
recovery_lost++;
// 削除する時用に記録しておく
len = (int)wcslen(recv_buf + recv_off);
wcscpy(recv2_buf + recv2_len, recv_buf + recv_off);
recv2_len += len + 1;
// 破損したリカバリ・ファイルはリストから取り除く (ファイル数はそのまま)
recv_len = remove_file_path(recv_buf, recv_len, recv_off);
}
fflush(stdout);
num++; // 次のファイルへ
}
/*{
FILE *fp;
fp = fopen("par_list.txt", "wb");
fwrite(recv_buf, 2, recv_len, fp);
fclose(fp);
fp = fopen("par_list2.txt", "wb");
fwrite(recv2_buf, 2, recv2_len, fp);
fclose(fp);
}*/
json_file_list(files); // JSONファイルに記録する
return recovery_lost;
}