DAT Format (Shadowcaster)

From ModdingWiki
Jump to navigation Jump to search

Overview

Shadowcaster contains two forms of DAT archive. This page is about the game's WAD-like game data archives. Another page details the cutscene DAT format.

A Shadowcaster game-data archive has the following general structure:

uint16_t  NumRecords;    // the number of sub-files contained in this archive.
uint32_t  Directory;     // an offset from the start of this file to the beginning of the record list.

uint8_t DataRegion[...]; // raw sub-file data. there can be hidden sections here; a sub-file's start does not have to begin right after the end of the previous.

// within each sub-file record is the following format:
uint32_t Offset;    // offset to the start of the data, from the beginning of the file.
uint32_t Size;      // the sub-file's size in bytes.
uint16_t NameOfs;   // a pointer from the beginning of the directory to a null-terminated string. this can be zero.
uint16_t Flags;     // in the floppy version, this is always zero. in the CD version, a value of 1 indicates RLE compression.

Record Names

In this format, sub-file records do not have to have a name.

The underlying logic of this is that the game will often request a specific named lump, then refer to unnamed records using an ID offset from that. For example, if the lump named "view" is lump #0, then the game can refer to "view+5" to refer to lump #5, and therefore, lump #5 does not have to store its name. In such a case, NameOfs = 0.

By convention, SLADE and MapCaster notate the names of these lumps as ID offsets from the last named entry found.

Reading a DAT Archive

Adapted from MapCaster GML:

NumRecords = buffer_read(buf, buffer_u16);
Directory = buffer_read(buf, buffer_u32);

buffer_seek(buf, buffer_seek_start, Directory);
for(z = 0; z < NumRecords; z++) {
    Records[z] = ds_map_create();
    
    r = Records[z];
    r[? "Offset"] = buffer_read(buf, buffer_u32);
    r[? "Size"] = buffer_read(buf, buffer_u32);
    r[? "NameOffset"] = buffer_read(buf, buffer_u16);
    r[? "Flags"] = buffer_read(buf, buffer_u16);
    
    // name all lumps
    if(r[? "NameOffset"] != 0) {
        last_name = 0;
        prev_ofs = buffer_tell(buf);
        
        // seek to the name table
        buffer_seek(buf, buffer_seek_start, Directory + r[? "NameOffset"]);

        r[? "Name"] = ReadString(...); // read until 0x00 is encountered
        
        // go back to where we were before in reading the directory
        buffer_seek(buf, buffer_seek_start, prev_ofs);
    }
    else {  // if the lump has no name, treat it as being
            // named as an offset from the previous named lump
        last_name++;
        l = Records[z - last_name];
        r[? "Name"] = l[? "Name"] + "+" + string(last_name);
    }
}