在存档中搜索文件并将其加载到内存中

Search for file in archive and load it into memory

基本上我需要将存档中的文件加载到内存中,但由于用户能够修改存档的内容,因此文件偏移量很可能会发生变化。

所以我需要创建一个函数,借助十六进制模式在存档中搜索文件,returns 文件偏移量,将文件加载到内存中,returns文件地址.

加载文件到内存和return地址我目前使用这个:

DWORD LoadBinary(char* filePath)
{
    FILE *file = fopen(filePath, "rb");
    long fileStart = ftell(file);
    fseek(file, 0, SEEK_END);
    long fileSize = ftell(file);
    fseek(file, fileStart, 0);
    BYTE *fileBuffer = new BYTE[fileSize];
    fread(fileBuffer, fileSize, 1, file);
    LPVOID newmem = VirtualAlloc(NULL, fileSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(newmem, fileBuffer, fileSize);
    delete[]fileBuffer;
    fclose(file);
    return (DWORD)newmem;
}

存档既未加密也未压缩,但它相当大(大约 1 GB),如果可能我不想将整个文件加载到内存中。

我知道我要在存档中查找的文件的大小,因此我不需要使用其他模式查找文件末尾的功能。

文件格式:“\x30\x00\x00\x00\xA0\x10\x04\x00”

文件长度:4096 字节

如何实现,需要哪些功能?

解决方案

代码对于大文件来说可能很慢,但这对我有用,因为我要查找的文件位于存档的开头。

FILE *file = fopen("C:/data.bin", "rb");
fseek(file, 0, SEEK_END);
long fileSize = ftell(file);
rewind(file);

BYTE *buffer = new BYTE[4];
int b = 0; //bytes read
long offset = 0;

for (int i = 0; i < fileSize; i++)
{
    int input = fgetc(file);

    *(int *)((DWORD)buffer + b) = input;

    if (b == 3)
    {
        b = 0;
    }
    else {
        b = b + 1;
    }

    if (buffer[0] == 0xDE & buffer[1] == 0xAD & buffer[2] == 0xBE & buffer[3] == 0xEF)
    {
        offset = (ftell(file) - 4);
        printf("Match @ 0x%08X", offset);
        break;
    }
}
fclose(file);

原理在this answer中陈述:你需要一个有限状态机(FSM),它将文件字节一个一个地作为输入,并根据FSM状态将当前输入与模式中的一个字节进行比较,这是模式中的索引。

这是最简单但天真的解决方案模板:

FILE *file = fopen(path, "rb");
size_t state = 0;
for (int input_result; (input_result = fgetc(file)) != EOF;) {
    char input = (char)input_result;
    if (input == pattern[state]) {
        ++state;
    } else {
        state = 0;
    }
    if (pattern_index == pattern_size) {
        // Pattern is found at (ftell(file) - pattern_size).
        break;
    }
}
fclose(file);

state变量在模式中占有一席之地,它是FSM的状态。

虽然此解决方案可以满足您的需求,但它并不是最优的,因为从文件中读取一个字节所花费的时间与读取更大的块(例如 512 字节甚至更多)所花费的时间几乎相同。您可以通过两个步骤自行改进:

  1. 每次迭代读取一个块,而不是单个字符。使用 fread()。请注意模式位置的计算(在找到之后)变得有点复杂,因为 ftell() 不再匹配 input 位置。
  2. 添加一个内部循环以遍历您刚刚阅读的块。以与以前相同的方式处理输入字符——这就是 FSM 方法证明自己有用的地方。