archive_structure.md 7.2 KB

Walrus RPG PIAF (PIAF is an Archive Format)

Control Header

WRPG_PIAF_MAGIC_HEADER : "WRPGPIAF"          <=> 8 bytes (not \0 terminated)
WRPG_PIAF_CHECKSUM     :  unsigned long long <=> 8 bytes
WRPG_PIAF_VERSION      :  unsigned int       <=> 4 bytes
WRPG_PIAF_NB_FILES     :  unsigned int       <=> 4 bytes
WRPG_PIAF_DATA_SIZE    :  unsigned int       <=> 4 bytes
<padding>              :  <filler>           <=> 4 bytes

Effective size : 32 bytes.

File table

WRPG_PIAF_FILENAME         : char[8]      <=> 8 bytes (not \0 terminated)
WRPG_PIAF_FILE_TYPE        : enumeration  <=> 4 bytes
WRPG_PIAF_COMPRESSION_TYPE : enumeration  <=> 4 bytes
WRPG_PIAF_FILE_SIZE        : unsigned int <=> 4 bytes
WPRG_PIAF_DATA_OFFSET      : unsigned int <=> 4 bytes // To being able to point directly to file data

Effective size = 24 bytes per file.

File types

0 = UNKNOWN
1 = MAP
2 = EVENT_LIST
3 = TEXT
4 = SPRITESHEET

Compression types

0 = RAW
1 = ZLIB
2 = RLE (?)

Archive draft

class / structure


struct WRPG_PIAF_FILE_ENTRY {
  char filename[9]; // or 9 if you want to put that \0.
  enum WRPG_PIAF_FILE_TYPE file_type;
  enum WRPG_PIAF_COMPRESSION_TYPE file_compresison;
  unsigned int file_size;
  unsigned int data_offset;
}

struct WRPG_PIAF_FILE {
  char magic_header[9];
  unsigned long long checksum;
   // 0xAABBCCCC form
   // AA = major version, BB = minor version, CC = hotfix
  unsigned int version;
  unsigned int nb_files;
  unsigned int data_size;
  unsigned int padidng;
  WRPG_PIAF_FILE_ENTRY* entries;
};

Loading

Here's the current state of the parser. Working on a x64 Linux. It should work everywhere because it doesn't care about endianess anymore.

Take note that it'll read big-endian only, so work on the file writer will have to be done to make it universal.

#include <iostream>
#include <cstring>

#define CURRENT_PIAF_VERSION 0x00000000
enum WRPG_PIAF_FILE_TYPE { FT_UNKNOWN, MAP, EVENT_LIST, TEXT, SPRITESHEET };
enum WRPG_PIAF_COMPRESSION_TYPE { COMP_UNKNOWN, RAW, ZLIB, RLE };

struct WRPG_PIAF_FILE_ENTRY {
  char filename[9]; // or 9 if you want to put that \0.
  enum WRPG_PIAF_FILE_TYPE file_type;
  enum WRPG_PIAF_COMPRESSION_TYPE file_compression;
  unsigned file_size;
  unsigned data_offset;
};

struct WRPG_PIAF_FILE {
  char magic_header[9];
  unsigned long long checksum;
  // 0xAABBCCCC form
  // AA = major version, BB = minor version, CC = hotfix
  unsigned version;
  unsigned nb_files;
  unsigned data_size;
  unsigned padding;
  WRPG_PIAF_FILE_ENTRY *entries;
};

unsigned read_unsigned(void *ptr) {
  unsigned result = 0;
  char *data = (char *)ptr;
  for (int i = 0; i < sizeof(unsigned); i++)
    result |= data[i] << (8 * (sizeof(unsigned) - 1 - i));
  return result;
}
unsigned read_unsigned_long_long(void *ptr) {
  unsigned long long result = 0;
  char *data = (char *)ptr;
  for (int i = 0; i < sizeof(unsigned long long); i++)
    result |= data[i] << (8 * (sizeof(unsigned long long) - 1 - i));
  return result;
}

struct WRPG_PIAF_FILE *load_structure_from_data(void *data, ssize_t size) {
  if (data == nullptr || size < 32) {
    fprintf(stderr, "Bad pointer/size\n");
    return nullptr; // NPE
  }

  char *data_char = (char *)data;
  if (strncmp(data_char, "WRPGPIAF", 8) != 0) {
    // BAD_COOKIE exception
    fprintf(stderr, "Bad version\n");
    return nullptr;
  }

  unsigned long long checksum = read_unsigned_long_long(&data_char[8]);
  bool check_ok = true;
  // checksum routine goes here
  if (!check_ok) {
    fprintf(stderr, "Bad checksum\n");
    // BAD_CHECKSUM exception
    return nullptr;
  }

  unsigned file_version = read_unsigned(&data_char[16]);
  if (file_version != CURRENT_PIAF_VERSION) {
    fprintf(stderr, "Bad file version\n");
    // BAD_VERSION exception
    // Or manage older versions.
    return nullptr;
  }

  unsigned nb_files = read_unsigned(&data_char[20]);
  printf("NB FILES : %d\n", nb_files);
  unsigned data_size = read_unsigned(&data_char[24]);

  if (data_size != size - 32 - 24 * nb_files) {
    // BAD_SIZE exception
    printf("Data size mismatch\n");
    return nullptr;
  }

  struct WRPG_PIAF_FILE_ENTRY *file_table = nullptr;
  if (nb_files > 0) {
    file_table = (WRPG_PIAF_FILE_ENTRY *)malloc(
        nb_files * sizeof(struct WRPG_PIAF_FILE_ENTRY));
    if (file_table == nullptr) {
      fprintf(stderr, "NPE Filetable\n");
      // NO_MORE_MEMORY exception
      return nullptr;
    }
    // I don't know if that works. It should because these structs are POD but
    // well...
    memcpy(file_table, &data_char[32], sizeof(WRPG_PIAF_FILE_ENTRY) * nb_files);
  }
  struct WRPG_PIAF_FILE *file =
      (WRPG_PIAF_FILE *)malloc(sizeof(struct WRPG_PIAF_FILE));
  if (file == nullptr) {
    // NO_MORE_MEMORY exception
    free(file_table);
    return nullptr;
  }

  // Now the structure is parsed.

  memcpy(file->magic_header, data_char, 8);
  for (size_t i = 0; i < nb_files; i++) {
    char *file_entry = &data_char[32 + 24 * i];
    memcpy(file_table[i].filename, file_entry, 8);
    file_table[i].filename[8] = '\0'; // Making sure that there is a terminator.
    file_table[i].file_type =
        static_cast<WRPG_PIAF_FILE_TYPE>(read_unsigned(&file_entry[8]));
    file_table[i].file_compression =
        static_cast<WRPG_PIAF_COMPRESSION_TYPE>(read_unsigned(&file_entry[12]));
    file_table[i].file_size = read_unsigned(&file_entry[16]);
    file_table[i].data_offset = read_unsigned(&file_entry[20]);
  }
  file->magic_header[8] = '\0';
  file->checksum = checksum;
  file->version = file_version;
  file->nb_files = nb_files;
  file->data_size = data_size;
  file->padding = 0XDEADBEEF;
  file->entries = file_table;

  return file;
}

int main(int argc, char const *argv[]) {
  /* code */
  char data[] = {'W', 'R', 'P', 'G', 'P', 'I', 'A', 'F', /*checksum*/ 0, 0, 0,
                 0, 0, 0, 0, 0, /*version*/ 0, 0, 0, 0, /*nb_files*/ 0, 0, 0, 2,
                 /*data size*/ 0, 0, 0, 1, /*padding*/ 0, 0, 0, 0,
                 /*File 1*/ 'T', 'e', 's', 't', 'i', 'n', 'g', '\0',
                 /*filetype*/ 0, 0, 0, MAP, /*file compression*/ 0, 0, 0, RAW,
                 /*filesize*/ 0, 0, 0, 1, /*offset*/ 0, 0, 0, 0,
                 /*File 2*/ 'T', 'e', 's', 't', 'i', 'n', 'g', '2',
                 /*filetype*/ 0, 0, 0, MAP, /*file compression*/ 0, 0, 0, RAW,
                 /*filesize*/ 0, 0, 0, 0, /*offset*/ 0, 0, 0, 0,
                 /* data*/ 42

  };
  WRPG_PIAF_FILE *archive = load_structure_from_data(data, sizeof(data));
  if (archive == nullptr) {
    printf("Failed\n");
  } else {
    printf("Result\n");
    printf("magic_header : %s\n", archive->magic_header);
    printf("checksum : 0x%llx\n", archive->checksum);
    printf("version : 0x%08x\n", archive->version);
    printf("nb_files : %u\n", archive->nb_files);
    printf("data_size : %u\n", archive->data_size);
    printf("entries : %x\n", archive->entries);
    for (size_t i = 0; i < archive->nb_files; i++) {
      printf("> File N°%d\n", i);
      printf(">   name : %s\n", archive->entries[i].filename);
      printf(">   file_type : %u\n", archive->entries[i].file_type);
      printf(">   file_size : %u\n", archive->entries[i].file_size);
      printf(">   data_offset : %u\n", archive->entries[i].data_offset);
      /* code */
    }
  }
  return 0;
}