/*
    bsp_map_extractor_fixed.c

    This program recursively searches from a given directory (or the current directory if none is provided)
    for files with the extension ".bsp". For each file it:
      - Opens the file in binary mode.
      - Reads its entire contents into a buffer.
      - Searches for the last occurrence of the pattern: "message"
      - Skips whitespace and extracts the quoted map name that follows.
      - Replaces any instance of the literal "\n" with four spaces.
      - Writes the full file path and the extracted map name to maplist.txt.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

#define MAX_BUFFER 2048

// Function declarations
void processDirectory(const char *directory, FILE *outFile);
void processFile(const char *filePath, FILE *outFile);
const char *find_last_pattern(const char *buffer, size_t bufSize, const char *pattern, size_t patSize);

int main(int argc, char *argv[]) {
    char searchDir[MAX_PATH];

    // Use directory provided as argument, or default to current directory.
    if (argc > 1) {
        strncpy(searchDir, argv[1], MAX_PATH - 1);
        searchDir[MAX_PATH - 1] = '\0';
    } else {
        if (!GetCurrentDirectory(MAX_PATH, searchDir)) {
            fprintf(stderr, "Error: Could not get current directory.\n");
            return 1;
        }
    }

    FILE *outFile = fopen("maplist.txt", "w");
    if (!outFile) {
        perror("fopen");
        return 1;
    }

    processDirectory(searchDir, outFile);

    fclose(outFile);
    printf("Extraction complete. Results written to maplist.txt\n");
    return 0;
}

// Recursively search directories.
void processDirectory(const char *directory, FILE *outFile) {
    char searchPath[MAX_PATH];
    WIN32_FIND_DATA findData;
    HANDLE hFind;

    snprintf(searchPath, MAX_PATH, "%s\\*", directory);
    hFind = FindFirstFile(searchPath, &findData);
    if (hFind == INVALID_HANDLE_VALUE)
        return;

    do {
        if (strcmp(findData.cFileName, ".") == 0 || 
            strcmp(findData.cFileName, "..") == 0)
            continue;

        char fullPath[MAX_PATH];
        snprintf(fullPath, MAX_PATH, "%s\\%s", directory, findData.cFileName);

        if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            processDirectory(fullPath, outFile);
        } else {
            char *ext = strrchr(findData.cFileName, '.');
            if (ext && _stricmp(ext, ".bsp") == 0) {
                processFile(fullPath, outFile);
            }
        }
    } while (FindNextFile(hFind, &findData));

    FindClose(hFind);
}

// Search the buffer for the last occurrence of a pattern without relying on null termination.
const char *find_last_pattern(const char *buffer, size_t bufSize, const char *pattern, size_t patSize) {
    const char *last = NULL;
    for (size_t i = 0; i <= bufSize - patSize; i++) {
        if (memcmp(buffer + i, pattern, patSize) == 0) {
            last = buffer + i;
        }
    }
    return last;
}

// Process a single .bsp file: extract and output the map name.
void processFile(const char *filePath, FILE *outFile) {
    FILE *fp = fopen(filePath, "rb");
    if (!fp) {
        fprintf(stderr, "Could not open file: %s\n", filePath);
        return;
    }

    // Get file size.
    fseek(fp, 0, SEEK_END);
    long fileSize = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    // Read entire file into buffer.
    char *buffer = (char *)malloc(fileSize);
    if (!buffer) {
        fclose(fp);
        fprintf(stderr, "Memory allocation error for file: %s\n", filePath);
        return;
    }

    if (fread(buffer, 1, fileSize, fp) != (size_t)fileSize) {
        fprintf(stderr, "Error reading file: %s\n", filePath);
        free(buffer);
        fclose(fp);
        return;
    }
    fclose(fp);

    // Find the last occurrence of "message"
    const char *pattern = "\"message\"";
    size_t patSize = strlen(pattern);
    const char *found = find_last_pattern(buffer, fileSize, pattern, patSize);

    char mapName[MAX_BUFFER] = {0};

    if (found) {
        const char *p = found + patSize;
        // Skip any whitespace characters.
        while (p < buffer + fileSize && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n'))
            p++;
        if (p < buffer + fileSize && *p == '\"') {
            p++; // Skip the opening quote.
            const char *start = p;
            // Find the closing quote.
            while (p < buffer + fileSize && *p != '\"') {
                p++;
            }
            size_t len = p - start;
            if (len >= MAX_BUFFER)
                len = MAX_BUFFER - 1;
            memcpy(mapName, start, len);
            mapName[len] = '\0';
        } else {
            strcpy(mapName, "Not found");
        }
    } else {
        strcpy(mapName, "Not found");
    }

    // Replace literal "\n" sequences with four spaces.
    char finalMapName[MAX_BUFFER * 2] = {0};
    const char *src = mapName;
    char *dest = finalMapName;
    while (*src) {
        if (src[0] == '\\' && src[1] == 'n') {
            memcpy(dest, "    ", 4);
            dest += 4;
            src += 2;
        } else {
            *dest++ = *src++;
        }
    }
    *dest = '\0';

    // Write the full file path and the map name to the output file.
    fprintf(outFile, "%s %s\n", filePath, finalMapName);

    free(buffer);
}
