#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "../include/StormLib.h"
void PrintError(const char *operation)
{
uint32_t error = SFileGetLastError();
printf("Failed to %s (error: %u)\n", operation, error);
}
bool EnumFilesCallback(const char *filename, void *user_data)
{
int *count = (int *)user_data;
printf(" [%d] %s\n", *count, filename);
(*count)++;
return true; }
void ReadFileExample(HANDLE hMpq, const char *filename)
{
printf("\n--- Reading file: %s ---\n", filename);
if (!SFileHasFile(hMpq, filename))
{
printf("File '%s' not found in archive\n", filename);
return;
}
HANDLE hFile = NULL;
if (!SFileOpenFileEx(hMpq, filename, 0, &hFile))
{
PrintError("open file");
return;
}
uint32_t sizeHigh = 0;
uint32_t sizeLow = SFileGetFileSize(hFile, &sizeHigh);
uint64_t fileSize = ((uint64_t)sizeHigh << 32) | sizeLow;
printf("File size: %llu bytes\n", (unsigned long long)fileSize);
char *buffer = (char *)malloc((size_t)fileSize + 1);
if (!buffer)
{
printf("Failed to allocate memory\n");
SFileCloseFile(hFile);
return;
}
uint32_t bytesRead = 0;
if (SFileReadFile(hFile, buffer, (uint32_t)fileSize, &bytesRead, NULL))
{
buffer[bytesRead] = '\0';
printf("Content (first %u bytes):\n", bytesRead > 100 ? 100 : bytesRead);
if (bytesRead > 100)
{
char temp = buffer[100];
buffer[100] = '\0';
printf("%s...\n", buffer);
buffer[100] = temp;
}
else
{
printf("%s\n", buffer);
}
}
else
{
PrintError("read file");
}
free(buffer);
SFileCloseFile(hFile);
}
void GetArchiveInfo(HANDLE hMpq)
{
printf("\n--- Archive Information ---\n");
uint64_t archiveSize = 0;
uint32_t sizeNeeded = 0;
if (SFileGetFileInfo(hMpq, 1, &archiveSize, sizeof(archiveSize), &sizeNeeded))
{
printf("Archive size: %llu bytes\n", (unsigned long long)archiveSize);
}
uint32_t hashTableSize = 0;
if (SFileGetFileInfo(hMpq, 2, &hashTableSize, sizeof(hashTableSize), &sizeNeeded))
{
printf("Hash table size: %u entries\n", hashTableSize);
}
uint32_t blockTableSize = 0;
if (SFileGetFileInfo(hMpq, 3, &blockTableSize, sizeof(blockTableSize), &sizeNeeded))
{
printf("Block table size: %u entries\n", blockTableSize);
}
uint32_t sectorSize = 0;
if (SFileGetFileInfo(hMpq, 4, §orSize, sizeof(sectorSize), &sizeNeeded))
{
printf("Sector size: %u bytes\n", sectorSize);
}
}
void EnumerateFiles(HANDLE hMpq)
{
printf("\n--- File Enumeration ---\n");
int fileCount = 0;
if (SFileEnumFiles(hMpq, "*", NULL, EnumFilesCallback, &fileCount))
{
printf("\nTotal files enumerated: %d\n", fileCount);
}
else
{
printf("Note: Archive may not have a (listfile)\n");
}
}
void TestFileOperations(HANDLE hMpq, const char *filename)
{
printf("\n--- File Operations Test: %s ---\n", filename);
HANDLE hFile = NULL;
if (!SFileOpenFileEx(hMpq, filename, 0, &hFile))
{
PrintError("open file for operations test");
return;
}
uint64_t position = 0;
uint32_t sizeNeeded = 0;
if (SFileGetFileInfo(hFile, 10, &position, sizeof(position), &sizeNeeded))
{
printf("Initial position: %llu\n", (unsigned long long)position);
}
char buffer[11] = {0};
uint32_t bytesRead = 0;
if (SFileReadFile(hFile, buffer, 10, &bytesRead, NULL))
{
printf("Read %u bytes: '%.10s'\n", bytesRead, buffer);
}
if (SFileGetFileInfo(hFile, 10, &position, sizeof(position), &sizeNeeded))
{
printf("Position after read: %llu\n", (unsigned long long)position);
}
uint32_t newPos = SFileSetFilePointer(hFile, 0, NULL, 0); printf("Position after seek to start: %u\n", newPos);
newPos = SFileSetFilePointer(hFile, 5, NULL, 0);
printf("Position after seek to 5: %u\n", newPos);
if (SFileReadFile(hFile, buffer, 10, &bytesRead, NULL))
{
buffer[bytesRead] = '\0';
printf("Read %u bytes from offset 5: '%.10s'\n", bytesRead, buffer);
}
SFileCloseFile(hFile);
}
void TestLocale()
{
printf("\n--- Locale Test ---\n");
uint32_t currentLocale = SFileGetLocale();
printf("Current locale: 0x%04X\n", currentLocale);
uint32_t oldLocale = SFileSetLocale(0x0409);
printf("Previous locale was: 0x%04X\n", oldLocale);
printf("New locale: 0x%04X\n", SFileGetLocale());
SFileSetLocale(oldLocale);
}
int main(int argc, char *argv[])
{
if (argc < 2)
{
printf("Storm FFI Example - StormLib Compatible MPQ Reader\n");
printf("Usage: %s <mpq_file> [file_to_read]\n", argv[0]);
printf("\nExample:\n");
printf(" %s test.mpq # List files and info\n", argv[0]);
printf(" %s test.mpq \"(listfile)\" # Read specific file\n", argv[0]);
printf(" %s test.mpq \"war3map.j\" # Read map script\n", argv[0]);
return 1;
}
HANDLE hMpq = NULL;
const char *archivePath = argv[1];
const char *fileToRead = argc >= 3 ? argv[2] : NULL;
printf("Opening archive: %s\n", archivePath);
if (!SFileOpenArchive(archivePath, 0, 0, &hMpq))
{
PrintError("open archive");
return 1;
}
printf("Successfully opened archive!\n");
char archiveName[260] = {0};
if (SFileGetArchiveName(hMpq, archiveName, sizeof(archiveName)))
{
printf("Archive path: %s\n", archiveName);
}
GetArchiveInfo(hMpq);
TestLocale();
if (fileToRead)
{
ReadFileExample(hMpq, fileToRead);
if (SFileHasFile(hMpq, fileToRead))
{
TestFileOperations(hMpq, fileToRead);
}
}
else
{
EnumerateFiles(hMpq);
const char *commonFiles[] = {
"(listfile)",
"(attributes)",
"(signature)",
"war3map.j",
"war3map.w3i",
NULL};
printf("\n--- Checking common files ---\n");
for (int i = 0; commonFiles[i] != NULL; i++)
{
if (SFileHasFile(hMpq, commonFiles[i]))
{
printf("Found: %s\n", commonFiles[i]);
if (strcmp(commonFiles[i], "(listfile)") == 0)
{
ReadFileExample(hMpq, commonFiles[i]);
}
}
}
}
printf("\n--- Error Handling Test ---\n");
SFileSetLastError(12345);
printf("Set error to: %u\n", SFileGetLastError());
HANDLE hBadFile = NULL;
if (!SFileOpenFileEx(hMpq, "this_file_does_not_exist.txt", 0, &hBadFile))
{
printf("Expected error for non-existent file: %u\n", SFileGetLastError());
}
printf("\nClosing archive...\n");
if (SFileCloseArchive(hMpq))
{
printf("Archive closed successfully.\n");
}
else
{
PrintError("close archive");
}
return 0;
}