Add files via upload

This commit is contained in:
Yutaka Sawada
2023-03-12 11:07:38 +09:00
committed by GitHub
parent ac6ea9a1b0
commit 4dcc2fe775
17 changed files with 4055 additions and 0 deletions

414
source/sfv_md5/ini.c Normal file
View File

@@ -0,0 +1,414 @@
// ini.c
// Copyright : 2022-02-16 Yutaka Sawada
// License : The MIT license
#ifndef _UNICODE
#define _UNICODE
#endif
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600 // Windows Vista or later
#endif
#include <malloc.h>
#include <stdio.h>
#include <windows.h>
#include "common.h"
#include "crc.h"
#include "ini.h"
/*
ファイル名は 0_#.bin (2+32+4 = 38文字)
「#」部分はチェックサム・ファイルを UTF-16 にエンコードした内容の
MD5ハッシュ値を 16進数表記にする。
ファイル・フォーマット
<!-- 検査結果ファイル識別用 -->
2: 検査結果の書式バージョン
4: 6バイト目以降からの CRC-32 チェックサム
<!-- チェックサム・セットの識別用 -->
4: チェックサム・ファイルの UTF-16 エンコード後の文字数
4: チェックサム・ファイルに記録されてるソース・ファイルの数
<!-- 検査状態をファイルごとに記録する -->
前半 12バイトでファイル項目を識別して、後半 18バイトに状態を保存する。
4: ボリュームのシリアル番号
8: ファイルのオブジェクトID
8: ソース・ファイルのサイズ
4: ソース・ファイルの作成日時
4: ソース・ファイルの更新日時
2: 下位 1-bit はファイルの状態 0=完全, 1=破損あるいはエラー
上位 15-bit はソース・ファイル番号 032767
*/
#define INI_VERSION 0x1270 // 検査結果の書式が決まった時のバージョン
#define REUSE_MIN 16384 // ファイル・サイズがこれより大きければ検査結果を利用する (16KB 以上 2GB 未満)
#define HEADER_SIZE 14
#define STATE_SIZE 30
#define STATE_READ 136
//#define VERBOSE 1 // 状態を冗長に出力する
static HANDLE hIniBin = NULL; // バイナリ・データ用の検査結果ファイル
static int ini_off;
// 検査結果をどのくらいの期間保存するか
int recent_data = 0;
/*
0 = 検査結果の再利用機能を無効にする(読み込まないし、記録もしない)
17= 前回の検査結果を読み込んで、今回のを記録する。
指定された期間よりも経過した古い記録は削除される。
1= 1日, 2= 3日, 3= 1週間, 4= 半月, 5= 1ヶ月, 6= 1年, 7= 無制限
+8 = 同じセットの記録を削除する、今回の結果は記録する。
他のセットは指定された期間よりも古いものだけ削除する。
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// File Time (UTC) を UNIX Time (UTC) に変換する
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];
unsigned int len, crc = 0xFFFFFFFF;
// 末尾まで読み込む
do {
if (!ReadFile(hFile, buf, 4096, &len, NULL) || (len == 0))
break;
// CRC-32 計算
crc = crc_update(crc, buf, len);
} while (len > 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;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// 検査結果ファイルを利用する為の関数
// 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, unsigned int set_len)
{
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;
// 設定ファイルのパス
wsprintf(ini_name, L"0_%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"0_*.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, &set_len, 4);
memcpy(set_data + 10, &file_num, 4);
if (match != 0){ // ID が同じでも Set 内容が異なる場合は初期化する
i = file_read_data(hIniBin, 0, set_data + HEADER_SIZE, HEADER_SIZE);
if ((set_data[HEADER_SIZE ] != (INI_VERSION >> 8)) ||
(set_data[HEADER_SIZE + 1] != (INI_VERSION & 0xFF))){
i = 1; // 古いバージョンの検査結果は参照しない
}
if (i == 0){ // 検査結果が破損してないか確かめる
if (SetFilePointer(hIniBin, 6, 0, FILE_BEGIN) != INVALID_SET_FILE_POINTER)
time_now = file_crc_part(hIniBin);
i = memcmp(&time_now, set_data + (HEADER_SIZE + 2), 4);
}
if (i == 0) // チェックサム・ファイルのデータを比較する
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_data(hIniBin, 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_data(hIniBin, 2, (unsigned char *)&old_crc, 4);
new_crc = file_crc_part(hIniBin);
if (new_crc != old_crc) // 検査結果が同じなら更新しない
file_write_data(hIniBin, 2, (unsigned char *)&new_crc, 4);
CloseHandle(hIniBin);
hIniBin = NULL;
}
}
// 検査結果が記録されてるかどうか
// -1=エラー, -2=記録なし, 0=完全, 2=破損
int check_ini_state(
int num, // ファイル番号
unsigned int meta[7], // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号
HANDLE hFile) // そのファイルのハンドル
{
unsigned char buf[STATE_SIZE * STATE_READ];
unsigned int rv, off, state;
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 {
meta[0] = 0;
meta[1] = 0;
return -1; // 属性の読み取りエラー
}
// ヘッダーの直後から開始する
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, meta + 4, 12) == 0){
ini_off += off; // 同じファイルの記録があった
state = 0;
memcpy(&state, buf + (off + 28), 2);
#ifdef VERBOSE
printf("check state, num = %d, offset = %d, state = %d\n", num, HEADER_SIZE + off, (state & 1) << 1);
#endif
// 番号が一致したら、ファイルのサイズと日時を比較する
if (((state >> 1) == (num & 0x7FFF)) && (memcmp(buf + (off + 12), meta, 16) == 0))
return (state & 1) << 1;
return -2; // 状態が変化してる
}
}
ini_off += rv;
}
ini_off = 0;
return -2; // これ以上の検査記録は無い
}
// ソース・ファイル状態は完全か破損だけ (消失だと検査しない)
void write_ini_state(
int num, // ファイル番号
unsigned int meta[7], // サイズ、作成日時、更新日時、ボリューム番号、オブジェクト番号
int state) // 状態、0=完全, 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;
}
}
// 今回の状態を書き込む
memcpy(buf, meta + 4, 12);
memcpy(buf + 12, meta, 16);
buf[28] = (unsigned char)((state >> 1) | (num << 1));
buf[29] = (unsigned char)(num >> 7);
#ifdef VERBOSE
printf("write state, num = %d, offset = %d, state = %d\n", num, ini_off, state);
#endif
if (file_write_data(hIniBin, ini_off, buf, STATE_SIZE) != 0){
delete_ini_file();
return;
}
}