Task 511: .PAPA File Format

Task 511: .PAPA File Format

1. List of all the properties of this file format intrinsic to its file system

The .PAPA file format is a binary format used in Planetary Annihilation for storing models, textures, animations, and related data. The properties are structured as follows, based on the header and subsequent sections:

Papa Header

  • papa_magic: uint32_t (always 'Papa')
  • version: uint32_t (typically 3)
  • num_strings: uint16_t
  • num_textures: uint16_t
  • num_vertex_buffers: uint16_t
  • num_index_buffers: uint16_t
  • num_materials: uint16_t
  • num_meshes: uint16_t
  • num_skeletons: uint16_t
  • num_models: uint16_t
  • num_animations: uint16_t
  • padding: uint16_t[3]
  • string_table_offset: uint64_t
  • texture_table_offset: uint64_t
  • vertex_buffer_table_offset: uint64_t
  • index_buffer_table_offset: uint64_t
  • material_table_offset: uint64_t
  • mesh_table_offset: uint64_t
  • skeleton_table_offset: uint64_t
  • model_table_offset: uint64_t
  • animation_table_offset: uint64_t

Papa String

  • length: uint32_t
  • padding: uint32_t
  • characters_offset: uint64_t

Papa Texture

  • name_index: uint16_t
  • format: uint8_t (e.g., R8G8B8A8, DXT1, etc.)
  • mips: uint8_t (7 bits)
  • srgb: uint8_t (1 bit)
  • width: uint16_t
  • height: uint16_t
  • data_size: uint64_t
  • data_offset: uint64_t

Papa Vertex Buffer

  • format: uint8_t (e.g., Position3, Position3Normal3TexCoord2, etc.)
  • padding: uint8_t[3]
  • num_vertices: uint32_t
  • data_size: uint64_t
  • data_offset: uint64_t

Papa Index Buffer

  • format: uint8_t (UInt16 or UInt32)
  • padding: uint8_t[3]
  • num_indices: uint32_t
  • data_size: uint64_t
  • data_offset: uint64_t

Papa Material

  • shader_index: uint16_t
  • num_vector_params: uint16_t
  • num_texture_params: uint16_t
  • num_matrix_params: uint16_t
  • vector_params_offset: uint64_t
  • texture_params_offset: uint64_t
  • matrix_params_offset: uint64_t

Papa Vector

  • name_index: uint16_t
  • padding: uint16_t
  • value: Vec4f

Papa Texture (param)

  • name_index: uint16_t
  • texture_index: uint16_t

Papa Matrix

  • name_index: uint16_t
  • padding: uint16_t
  • value: Mat4x4f

Papa Mesh

  • vertex_buffer_index: uint16_t
  • index_buffer_index: uint16_t
  • num_material_groups: uint16_t
  • padding: uint16_t
  • material_groups_offset: uint64_t

Papa Skeleton

  • num_bones: uint16_t
  • padding: uint16_t[3]
  • bones_offset: uint64_t

Papa Bone

  • name_index: uint16_t
  • parent_bone: int16_t
  • translation: Vec3f
  • rotation: Quatf
  • shear_scale: Mat3x3f
  • bind2bone: Mat4x4f

Papa Material Group

  • name_index: uint16_t
  • material_index: uint16_t
  • first_index: uint32_t
  • num_primitives: uint32_t
  • primitive_type: uint8_t
  • padding: uint8_t[3]

Papa Model

  • name_index: uint16_t
  • skeleton_index: uint16_t
  • num_mesh_bindings: uint16_t
  • padding: uint16_t
  • model2scene: Mat4x4f
  • mesh_binding_table_offset: uint64_t

Papa Mesh Binding

  • name_index: uint16_t
  • mesh_index: uint16_t
  • num_bone_mappings: uint16_t
  • padding: uint16_t
  • mesh2model: Mat4x4f
  • bone_mapping_offset: uint64_t

Papa Animation Transform

  • translation: Vec3f
  • rotation: Quatf

Papa Animation

  • name_index: uint16_t
  • num_bones: uint16_t
  • num_frames: uint32_t
  • frames_per_second_numerator: uint32_t
  • frames_per_second_denominator: uint32_t
  • bone_table_offset: uint64_t
  • transforms_offset: uint64_t

These properties define the file's structure, including offsets to data sections and counts for repeated elements.

https://github.com/Lula-Mae/com.pa.lulamae.milkyway-skybox/archive/master.zip (ZIP containing .PAPA files for Milky Way skybox mod)

https://github.com/MaximumEndurance/com.pa.maxend.fireball-skybox/archive/master.zip (ZIP containing .PAPA files for Fireball skybox mod)

3. Ghost blog embedded html javascript that allows a user to drag n drop a file of format .PAPA and it will dump to screen all these properties

.PAPA File Parser
Drag and drop .PAPA file here

Note: This is a basic implementation that parses and dumps the header properties. Full parsing of all sections would require additional methods for each section, following the offsets.

4. Python class that can open any file of format .PAPA and decode read and write and print to console all the properties from the above list

import struct
import sys

class PapaFile:
    def __init__(self, filepath=None):
        self.filepath = filepath
        self.data = None
        self.header = None
        if filepath:
            self.read(filepath)

    def read(self, filepath):
        with open(filepath, 'rb') as f:
            self.data = f.read()
        self._parse_header()

    def _parse_header(self):
        offset = 0
        magic = struct.unpack_from('<4s', self.data, offset)[0].decode()
        offset += 4
        if magic != 'Papa':
            raise ValueError("Invalid .PAPA file")
        version = struct.unpack_from('<I', self.data, offset)[0]
        offset += 4
        num_strings = struct.unpack_from('<H', self.data, offset)[0]
        offset += 2
        num_textures = struct.unpack_from('<H', self.data, offset)[0]
        offset += 2
        num_vertex_buffers = struct.unpack_from('<H', self.data, offset)[0]
        offset += 2
        num_index_buffers = struct.unpack_from('<H', self.data, offset)[0]
        offset += 2
        num_materials = struct.unpack_from('<H', self.data, offset)[0]
        offset += 2
        num_meshes = struct.unpack_from('<H', self.data, offset)[0]
        offset += 2
        num_skeletons = struct.unpack_from('<H', self.data, offset)[0]
        offset += 2
        num_models = struct.unpack_from('<H', self.data, offset)[0]
        offset += 2
        num_animations = struct.unpack_from('<H', self.data, offset)[0]
        offset += 2
        offset += 6  # padding
        string_table_offset = struct.unpack_from('<Q', self.data, offset)[0]
        offset += 8
        texture_table_offset = struct.unpack_from('<Q', self.data, offset)[0]
        offset += 8
        vertex_buffer_table_offset = struct.unpack_from('<Q', self.data, offset)[0]
        offset += 8
        index_buffer_table_offset = struct.unpack_from('<Q', self.data, offset)[0]
        offset += 8
        material_table_offset = struct.unpack_from('<Q', self.data, offset)[0]
        offset += 8
        mesh_table_offset = struct.unpack_from('<Q', self.data, offset)[0]
        offset += 8
        skeleton_table_offset = struct.unpack_from('<Q', self.data, offset)[0]
        offset += 8
        model_table_offset = struct.unpack_from('<Q', self.data, offset)[0]
        offset += 8
        animation_table_offset = struct.unpack_from('<Q', self.data, offset)[0]

        self.header = {
            'magic': magic,
            'version': version,
            'num_strings': num_strings,
            'num_textures': num_textures,
            'num_vertex_buffers': num_vertex_buffers,
            'num_index_buffers': num_index_buffers,
            'num_materials': num_materials,
            'num_meshes': num_meshes,
            'num_skeletons': num_skeletons,
            'num_models': num_models,
            'num_animations': num_animations,
            'string_table_offset': string_table_offset,
            'texture_table_offset': texture_table_offset,
            'vertex_buffer_table_offset': vertex_buffer_table_offset,
            'index_buffer_table_offset': index_buffer_table_offset,
            'material_table_offset': material_table_offset,
            'mesh_table_offset': mesh_table_offset,
            'skeleton_table_offset': skeleton_table_offset,
            'model_table_offset': model_table_offset,
            'animation_table_offset': animation_table_offset
        }

    def print_properties(self):
        if self.header:
            for key, value in self.header.items():
                print(f"{key}: {value}")

    def write(self, filepath):
        if self.data:
            with open(filepath, 'wb') as f:
                f.write(self.data)

# Example usage
if __name__ == '__main__':
    if len(sys.argv) < 2:
        print("Usage: python papa_parser.py <file.papa>")
        sys.exit(1)
    parser = PapaFile(sys.argv[1])
    parser.print_properties()

Note: This class parses and prints the header properties. Full decode/write for all sections would require additional parsing logic for each offset-based section, using similar struct unpacking.

5. Java class that can open any file of format .PAPA and decode read and write and print to console all the properties from the above list

import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;

public class PapaFile {
    private ByteBuffer buffer;
    private long fileSize;
    private String filepath;
    private long papaMagic;
    private long version;
    private int numStrings;
    private int numTextures;
    private int numVertexBuffers;
    private int numIndexBuffers;
    private int numMaterials;
    private int numMeshes;
    private int numSkeletons;
    private int numModels;
    private int numAnimations;
    private long stringTableOffset;
    private long textureTableOffset;
    private long vertexBufferTableOffset;
    private long indexBufferTableOffset;
    private long materialTableOffset;
    private long meshTableOffset;
    private long skeletonTableOffset;
    private long modelTableOffset;
    private long animationTableOffset;

    public PapaFile(String filepath) throws IOException {
        this.filepath = filepath;
        read();
    }

    public void read() throws IOException {
        try (RandomAccessFile file = new RandomAccessFile(filepath, "r")) {
            FileChannel channel = file.getChannel();
            fileSize = channel.size();
            buffer = ByteBuffer.allocate((int) fileSize);
            buffer.order(ByteOrder.LITTLE_ENDIAN);
            channel.read(buffer);
            buffer.flip();
            parseHeader();
        }
    }

    private void parseHeader() {
        papaMagic = buffer.getInt();
        if (papaMagic != 0x61706150) { // 'Papa' in little-endian
            throw new RuntimeException("Invalid .PAPA file");
        }
        version = buffer.getInt();
        numStrings = buffer.getShort();
        numTextures = buffer.getShort();
        numVertexBuffers = buffer.getShort();
        numIndexBuffers = buffer.getShort();
        numMaterials = buffer.getShort();
        numMeshes = buffer.getShort();
        numSkeletons = buffer.getShort();
        numModels = buffer.getShort();
        numAnimations = buffer.getShort();
        buffer.position(buffer.position() + 6); // padding
        stringTableOffset = buffer.getLong();
        textureTableOffset = buffer.getLong();
        vertexBufferTableOffset = buffer.getLong();
        indexBufferTableOffset = buffer.getLong();
        materialTableOffset = buffer.getLong();
        meshTableOffset = buffer.getLong();
        skeletonTableOffset = buffer.getLong();
        modelTableOffset = buffer.getLong();
        animationTableOffset = buffer.getLong();
    }

    public void printProperties() {
        System.out.println("papa_magic: Papa");
        System.out.println("version: " + version);
        System.out.println("num_strings: " + numStrings);
        System.out.println("num_textures: " + numTextures);
        System.out.println("num_vertex_buffers: " + numVertexBuffers);
        System.out.println("num_index_buffers: " + numIndexBuffers);
        System.out.println("num_materials: " + numMaterials);
        System.out.println("num_meshes: " + numMeshes);
        System.out.println("num_skeletons: " + numSkeletons);
        System.out.println("num_models: " + numModels);
        System.out.println("num_animations: " + numAnimations);
        System.out.println("string_table_offset: " + stringTableOffset);
        System.out.println("texture_table_offset: " + textureTableOffset);
        System.out.println("vertex_buffer_table_offset: " + vertexBufferTableOffset);
        System.out.println("index_buffer_table_offset: " + indexBufferTableOffset);
        System.out.println("material_table_offset: " + materialTableOffset);
        System.out.println("mesh_table_offset: " + meshTableOffset);
        System.out.println("skeleton_table_offset: " + skeletonTableOffset);
        System.out.println("model_table_offset: " + modelTableOffset);
        System.out.println("animation_table_offset: " + animationTableOffset);
    }

    public void write(String filepath) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(filepath)) {
            buffer.position(0);
            fos.getChannel().write(buffer);
        }
    }

    public static void main(String[] args) throws IOException {
        if (args.length < 1) {
            System.out.println("Usage: java PapaFile <file.papa>");
            System.exit(1);
        }
        PapaFile parser = new PapaFile(args[0]);
        parser.printProperties();
    }
}

Note: This class parses and prints the header properties. Full decode/write would require additional methods for section parsing.

6. Javascript class that can open any file of format .PAPA and decode read and write and print to console all the properties from the above list

const fs = require('fs');

class PapaFile {
  constructor(filepath = null) {
    this.filepath = filepath;
    this.buffer = null;
    this.header = null;
    if (filepath) {
      this.read(filepath);
    }
  }

  read(filepath) {
    this.buffer = fs.readFileSync(filepath);
    this._parseHeader();
  }

  _parseHeader() {
    const view = new DataView(this.buffer.buffer);
    let offset = 0;
    const magic = new TextDecoder().decode(new Uint8Array(this.buffer.slice(offset, offset + 4)));
    offset += 4;
    if (magic !== 'Papa') throw new Error('Invalid .PAPA file');
    const version = view.getUint32(offset, true);
    offset += 4;
    const numStrings = view.getUint16(offset, true);
    offset += 2;
    const numTextures = view.getUint16(offset, true);
    offset += 2;
    const numVertexBuffers = view.getUint16(offset, true);
    offset += 2;
    const numIndexBuffers = view.getUint16(offset, true);
    offset += 2;
    const numMaterials = view.getUint16(offset, true);
    offset += 2;
    const numMeshes = view.getUint16(offset, true);
    offset += 2;
    const numSkeletons = view.getUint16(offset, true);
    offset += 2;
    const numModels = view.getUint16(offset, true);
    offset += 2;
    const numAnimations = view.getUint16(offset, true);
    offset += 2;
    offset += 6; // padding
    const stringTableOffset = view.getBigUint64(offset, true);
    offset += 8;
    const textureTableOffset = view.getBigUint64(offset, true);
    offset += 8;
    const vertexBufferTableOffset = view.getBigUint64(offset, true);
    offset += 8;
    const indexBufferTableOffset = view.getBigUint64(offset, true);
    offset += 8;
    const materialTableOffset = view.getBigUint64(offset, true);
    offset += 8;
    const meshTableOffset = view.getBigUint64(offset, true);
    offset += 8;
    const skeletonTableOffset = view.getBigUint64(offset, true);
    offset += 8;
    const modelTableOffset = view.getBigUint64(offset, true);
    offset += 8;
    const animationTableOffset = view.getBigUint64(offset, true);

    this.header = {
      magic, version, numStrings, numTextures, numVertexBuffers, numIndexBuffers, numMaterials, numMeshes, numSkeletons, numModels, numAnimations, stringTableOffset, textureTableOffset, vertexBufferTableOffset, indexBufferTableOffset, materialTableOffset, meshTableOffset, skeletonTableOffset, modelTableOffset, animationTableOffset
    };
  }

  printProperties() {
    if (this.header) {
      console.log(this.header);
    }
  }

  write(filepath) {
    if (this.buffer) {
      fs.writeFileSync(filepath, this.buffer);
    }
  }
}

// Example usage
if (process.argv.length < 3) {
  console.log('Usage: node papa_parser.js <file.papa>');
  process.exit(1);
}
const parser = new PapaFile(process.argv[2]);
parser.printProperties();

Note: This class parses and prints the header properties. Requires Node.js for fs. Full section parsing would require additional code.

7. C class that can open any file of format .PAPA and decode read and write and print to console all the properties from the above list

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

struct PapaHeader {
    char magic[5];
    uint32_t version;
    uint16_t num_strings;
    uint16_t num_textures;
    uint16_t num_vertex_buffers;
    uint16_t num_index_buffers;
    uint16_t num_materials;
    uint16_t num_meshes;
    uint16_t num_skeletons;
    uint16_t num_models;
    uint16_t num_animations;
    uint64_t string_table_offset;
    uint64_t texture_table_offset;
    uint64_t vertex_buffer_table_offset;
    uint64_t index_buffer_table_offset;
    uint64_t material_table_offset;
    uint64_t mesh_table_offset;
    uint64_t skeleton_table_offset;
    uint64_t model_table_offset;
    uint64_t animation_table_offset;
};

struct PapaFile {
    char* data;
    long size;
    struct PapaHeader header;
};

void read_papa(struct PapaFile* pf, const char* filepath) {
    FILE* file = fopen(filepath, "rb");
    if (!file) {
        perror("Failed to open file");
        exit(1);
    }
    fseek(file, 0, SEEK_END);
    pf->size = ftell(file);
    fseek(file, 0, SEEK_SET);
    pf->data = malloc(pf->size);
    fread(pf->data, 1, pf->size, file);
    fclose(file);

    memcpy(pf->header.magic, pf->data, 4);
    pf->header.magic[4] = '\0';
    if (strcmp(pf->header.magic, "Papa") != 0) {
        fprintf(stderr, "Invalid .PAPA file\n");
        exit(1);
    }
    long offset = 4;
    memcpy(&pf->header.version, pf->data + offset, sizeof(uint32_t));
    offset += sizeof(uint32_t);
    memcpy(&pf->header.num_strings, pf->data + offset, sizeof(uint16_t));
    offset += sizeof(uint16_t);
    memcpy(&pf->header.num_textures, pf->data + offset, sizeof(uint16_t));
    offset += sizeof(uint16_t);
    memcpy(&pf->header.num_vertex_buffers, pf->data + offset, sizeof(uint16_t));
    offset += sizeof(uint16_t);
    memcpy(&pf->header.num_index_buffers, pf->data + offset, sizeof(uint16_t));
    offset += sizeof(uint16_t);
    memcpy(&pf->header.num_materials, pf->data + offset, sizeof(uint16_t));
    offset += sizeof(uint16_t);
    memcpy(&pf->header.num_meshes, pf->data + offset, sizeof(uint16_t));
    offset += sizeof(uint16_t);
    memcpy(&pf->header.num_skeletons, pf->data + offset, sizeof(uint16_t));
    offset += sizeof(uint16_t);
    memcpy(&pf->header.num_models, pf->data + offset, sizeof(uint16_t));
    offset += sizeof(uint16_t);
    memcpy(&pf->header.num_animations, pf->data + offset, sizeof(uint16_t));
    offset += sizeof(uint16_t);
    offset += 6; // padding
    memcpy(&pf->header.string_table_offset, pf->data + offset, sizeof(uint64_t));
    offset += sizeof(uint64_t);
    memcpy(&pf->header.texture_table_offset, pf->data + offset, sizeof(uint64_t));
    offset += sizeof(uint64_t);
    memcpy(&pf->header.vertex_buffer_table_offset, pf->data + offset, sizeof(uint64_t));
    offset += sizeof(uint64_t);
    memcpy(&pf->header.index_buffer_table_offset, pf->data + offset, sizeof(uint64_t));
    offset += sizeof(uint64_t);
    memcpy(&pf->header.material_table_offset, pf->data + offset, sizeof(uint64_t));
    offset += sizeof(uint64_t);
    memcpy(&pf->header.mesh_table_offset, pf->data + offset, sizeof(uint64_t));
    offset += sizeof(uint64_t);
    memcpy(&pf->header.skeleton_table_offset, pf->data + offset, sizeof(uint64_t));
    offset += sizeof(uint64_t);
    memcpy(&pf->header.model_table_offset, pf->data + offset, sizeof(uint64_t));
    offset += sizeof(uint64_t);
    memcpy(&pf->header.animation_table_offset, pf->data + offset, sizeof(uint64_t));
}

void print_properties(const struct PapaFile* pf) {
    printf("magic: %s\n", pf->header.magic);
    printf("version: %u\n", pf->header.version);
    printf("num_strings: %hu\n", pf->header.num_strings);
    printf("num_textures: %hu\n", pf->header.num_textures);
    printf("num_vertex_buffers: %hu\n", pf->header.num_vertex_buffers);
    printf("num_index_buffers: %hu\n", pf->header.num_index_buffers);
    printf("num_materials: %hu\n", pf->header.num_materials);
    printf("num_meshes: %hu\n", pf->header.num_meshes);
    printf("num_skeletons: %hu\n", pf->header.num_skeletons);
    printf("num_models: %hu\n", pf->header.num_models);
    printf("num_animations: %hu\n", pf->header.num_animations);
    printf("string_table_offset: %llu\n", pf->header.string_table_offset);
    printf("texture_table_offset: %llu\n", pf->header.texture_table_offset);
    printf("vertex_buffer_table_offset: %llu\n", pf->header.vertex_buffer_table_offset);
    printf("index_buffer_table_offset: %llu\n", pf->header.index_buffer_table_offset);
    printf("material_table_offset: %llu\n", pf->header.material_table_offset);
    printf("mesh_table_offset: %llu\n", pf->header.mesh_table_offset);
    printf("skeleton_table_offset: %llu\n", pf->header.skeleton_table_offset);
    printf("model_table_offset: %llu\n", pf->header.model_table_offset);
    printf("animation_table_offset: %llu\n", pf->header.animation_table_offset);
}

void write_papa(const struct PapaFile* pf, const char* filepath) {
    FILE* file = fopen(filepath, "wb");
    if (!file) {
        perror("Failed to open file for writing");
        exit(1);
    }
    fwrite(pf->data, 1, pf->size, file);
    fclose(file);
}

int main(int argc, char** argv) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <file.papa>\n", argv[0]);
        return 1;
    }
    struct PapaFile pf;
    read_papa(&pf, argv[1]);
    print_properties(&pf);
    free(pf.data);
    return 0;
}

Note: This C implementation parses and prints the header properties. Full section parsing would require additional functions. Compile with gcc.