zzrtl uses a very minimalistic C implementation. The following C features are unsupported:  - floating points         - for loops  - the preprocessor        - goto  - do-while                - all variables must be  - break                     declared at beginning  - +=, -=, etc.              of function  - unsigned types          - arrays  - declare-anywhere        - assigning variables  - switch-case               during declaration  - the struct keyword is   - function prototypes   reserved for built-in   - probably other stuff;   types                     proceed with caution Besides what you write yourself, the only functions available to you are the ones listed below. A brief description is provided for each. zzrtl's built-in error checking reports errors and ends execution if anything goes wrong, though there are exceptions. If you find these notes too cryptic, refer to the pre-made scripts. They are thoroughly commented and show each function in action. libc functions: printf sprintf sscanf malloc free fopen fprintf fclose memset memcmp strcmp strcasecmp strcat system (most of these have had error checking added so the program will  throw an error if an invalid pointer is used with them) generic functions: die(char *fmt, ...)    terminate with a custom error message get32(void *)          get 32-bit (4 byte) value from raw data get24(void *)          get 24-bit (3 byte) value from raw data get16(void *)          get 16-bit (2 byte) value from raw data get8(void *)           get 8-bit (1 byte) value from raw data put32(void *, int)     put 32-bit (4 byte) value into raw data put24(void *, int)     put 24-bit (3 byte) value into raw data put16(void *, int)     put 16-bit (2 byte) value into raw data put8(void *, int)      put 8-bit (1 byte) value into raw data u8(char)               get unsigned equivalent of signed char s8(int)                get signed char equivalent of int u16(int)               cast int to unsigned short s16(int)               cast int to signed short u32op(a, *op, b)       cast a and b to u32 and do operation;                        example: u32op(apples, ">=", oranges)                        the reason this exists is as a workaround for                        situations where signedness may cause behavior                        that is undesirable;                        valid options for op:                        "+" "-" "*" "/" "&" "|" "<" ">" "<="                        ">=" "==" "%" substring(*list, idx)  get string at index `idx` in string `*list`;                        returns 0 if anything goes awry find_bytes             find ndl inside hay; ndl is a 0-term'd string   (*hay, hay_sz       of hexadecimal characters (failure returns 0)    , *ndl, only1)     NOTE: * can be used for wildcard bytes                              example string "DEAD****BEEF"                        only1 = 1 throws fatal error if more than one                                occurrence is found find_bytes_stride      same as find_bytes(), but allows you to specify   (*hay, hay_sz, *ndl stride; stride is the number of bytes to    , stride, only1)   advance when searching find_text              find ndl inside hay; ndl is a text string   (*hay, hay_sz       (returns 0 on failure)    , *ndl, only1)     example string "scubadiver"                        only1 = 1 throws fatal error if more than one                                occurrence is found find_text_stride       same as find_text(), but allows you to specify   (*hay, hay_sz, *ndl stride; stride is the number of bytes to   , stride, only1)    advance when searching ovl_vram_sz            returns virtual ram size of overlay   (void *, sz) load_png               returns pointer to rgba8888 pixel data of png   (fn, int *w, int *h) if it is successfully loaded; w and h are                        propagated with its width and height as well                        ex: pix = load_png("sky.png", &width, &height);                        returns 0 if file doesn't exist int_array(num, ...)    create an int array containing `num` elements                        array32 = int_array(4, 10, 20, 30, 40);                        array32[0] is now 10, [1] is 20, and so on new_string(..., 0)     combines multiple strings and returns a pointer                        to the result, which you can free() if you want                        the list you provide must end with 0                        ex: new_string("build-", codec, ".z64", 0); loadfile(              load a file, returning a pointer to its raw   char *fn, int *sz   data, or 0 if it doesn't exist; if `optional`    , bool optional)   is `true`, no error will be thrown if file does                        not exist; `sz` will be set to size of file (in                        bytes), or if you don't need that, pass 0 tsv_col_row(char *tsv  returns pointer to string inside tsv, beneath   , char *col         column `col` and in row `row` (row 0 is first   , int  row)        row where names are contained) directory functions: dir_exists(char *)     returns 1 if directory of given name exists,                        returns 0 otherwise dir_enter(char *)      enter a directory (folder)                        NOTE: directory is created if it doesn't exist dir_enter_file(char *) enter the directory of provided file dir_leave(void)        leave last-entered directory dir_mkname             make a compliant directory name; str can be 0   (int, *str)         if you want only a number for the folder name dir_use_style(char *)  set style used by dir_mkname (default is "pre")                        valid options:                           "pre"  : uses form "%d - %s"                           "preX" : uses form "0x%X - %s"                           "post" : uses form "%s (%d)"                           "postX" : uses form "%s (0x%X)" file_exists(char *)    returns non-zero if file of given name exists,                        returns 0 otherwise struct rom functions: .new(char *)           allocate a rom structure and load rom file .free(void)            free a rom structure .raw(ofs)              get pointer to raw data at offset `ofs` in rom .save(char *)          save rom to disk using specified filename .align(int)            align injected files such that their injection                        offset is a multiple of the value you provide;                        in retail roms, alignment is 0x1000 for every                        file except the overlays, boot, dmadata, the                        audio files, and link_animetion [sic] (it is                        0x10 for all of these); the value you provide                        must be a multiple of 16 (0x10); to maximize                        space savings, inject files with smaller                        alignment requirements first .compress(fmt, mb)     compress rom using specified algorithm                        valid options: "yaz", "lzo", "ucl", "aplib"                        NOTE: to use another codec, patch your rom;                        mb is the number of mb to cap the compressed                        rom to; 32 is standard for OoT and MM .seek(u32)             go to offset within rom .seek_cur(adv)         go forward adv bytes in rom                        NOTE: use a negative value to travel backwards .tell(void)            get offset within rom .size(void)            get size of rom .inject(fn, comp)      inject file into rom                        NOTE: if comp is non-zero, file is compressible                        NOTE: returns pointer to injected data, or 0                        NOTE: if name is formatted like "*.ext", it                              will auto-detect a file by extension; for                              example, "*.zobj" to inject whatever zobj                              it can find .inject_dma            inject file into rom, over known dma index   (fn, comp , idx )     (file-size must match file being overwritten)
.inject_png            loads PNG, converts to N64 format, and injects   (fn, fmt, bpp, comp) into rom fmt and bpp use the n64texconv enums                        NOTE: if comp is non-zero, file is compressible .inject_raw            inject raw data into rom   (*raw, sz, comp)    NOTE: if comp is non-zero, file is compressible                        NOTE: returns pointer to injected data, or 0                        NOTE: sz is number of bytes, raw points to them .inject_raw_dma        inject raw data into rom, over known dma index   (*raw, sz, idx, cmp) (sz must match entry being overwritten) .file_start(void)      get start offset of data injected with inject()                        NOTE: will be 0 if inject() failed .file_end(void)        get end offset of data injected with inject()                        NOTE: will be 0 if inject() failed .file(void)            get pointer to data of most recently injected;                        NOTE: will be 0 if inject() failed .file_sz(void)         get size of injected data                        NOTE: will be 0 if inject() failed .file_dma(void)        get dma index of data injected with inject()                        NOTE: will be -1 if inject failed .extract               extract raw data to a file   (fn, start, end) .extract_png           converts raw texture data to rgba8888, saves   (fn, buf*, tex, pal as PNG; buf can be a prealloc'd block that you    w, h, fmt, bpp)    can guarantee is large enough to intermediately                        store the converted texture, or 0 to tell the                        function to alloc its own; fmt and bpp must be                        as they are defined by the n64texconv enums .dma(start, num)       specify start of dmadata, and number of entries                      * every entry is by default marked as readonly,                        and dma_queue() must be used to mark specific                        entries as writable                      * no entry is queued for compression at first,                        and dma_compress() must be used to selectively                        enable compression where it is desired .dma_queue_one(idx)    mark one dma entry (by index) as writable; .dma_queue             mark dma indices as writable, between start and   (start, end)        end, inclusive (aka start <= idx <= end) .dma_compress_one      set compression flag on dma entry (by index);   (idx, comp)         if (comp == 1), file is marked for compression;                        0 means no compression; other non-zero values                        are reserved for internal use only .dma_compress          set compression flag on indices between start   (start, end, comp)  and end, inclusive (aka start <= idx <= end) .dma_ready(void)       call this when you're finished with the dma                        stuff; must be called before you inject data .write(void *, sz)     write and advance `sz` bytes within rom .write32(u32)          write and advance four bytes within rom .write24(int)          write and advance three bytes within rom .write16(int)          write and advance two  bytes within rom .write8(int)           write and advance one  byte within rom .read32(void)          read and advance four bytes within rom .read24(void)          read and advance three bytes within rom .read16(void)          read and advance two  bytes within rom .read8(void)           read and advance one  byte within rom .cloudpatch(ofs, fn)   patch a rom with a cloudpatch (.txt patch);                        ofs is the value to add to all the offsets in                        the patch, which is useful for applying patches                        to individual files as they are injected;                        otherwise, just use 0 .rearchive             this function is for MM only; it re-encodes   (start, end, old,   the contents of an archive (a file containing    new, repack)       multiple compressed files); `old` refers to                        the old encoding (only "yaz" is supported for                        now), and `new` refers to the new encoding,                        like "ucl"; `repack` should always be true (1)                        unless you run out of space and need the lossy
                       packing hack (use false (0) in that case)
                       (it is lossy in that it zeroes data at the end of
                       the texture until it fits, so it is recommended
                       that you optimize your textures manually)
.rearchive_one         re-encode one archive, by dma index   (idx, old, new,     (MM only; see rearchive for more details)    repack) struct folder functions: .new(*ext)             allocate a folder structure and parse the                        current directory; contents can be named any                      . way you like, as long as they contain a number                         > "0 - gameplay_keep"                         > "gameplay_keep (0)"                         > "room_0.zmap"                         > etc.                      . folder/file names starting with a '.' or '_'                        are not processed                         > .trash                         > _src                         > etc.                      . items are accessed in the order specified by                        the numerical part, which can be hexadecimal as                        long as it is preceded by "0x"                      . no two folder/file names are allowed to contain                        the same number (a fatal error will be thrown)                      . if ext is 0, it creates a folder list                      . if ext is non-zero, it creates a list of files                        of the requested extension, such as "zmap"                      . it first tries to find a number at the very                        beginning (it is not allowed to be surrounded                        by quotes, parenthesis, preceded by a space,                        etc); if that fails, it uses the last number it                        detects in the name                         -> "1 - spot04"       yields index 1                         -> "En_Torch2 (51)"   yields index 51                         -> "object_link_boy"  throws fatal error .free(void)            free a folder structure .name(void)            get current folder name .index(void)           get numerical part of current folder name .next(void)            go to next folder in list;                        returns 0 when end of list is reached .count(void)           get number of items in list .remaining(void)       get number of items between current and end .max(void)             get highest index detected in list struct conf functions: .new(*fn, *fmt)        allocate a conf structure and parse file;                        fmt should be either "table" or "list";                      * a fatal error occurs if the file doesn't exist                      * see conf section for more details .free(void)            free a conf structure .exists(*name)         returns non-zero if name exists .get_int(*name)        get integer value associated with name;                      * this function throws a fatal error if the name                        does not exist; if you are handling optional                        names, use .exists(name) to confirm it exists                        before calling this function                      * passing 0 to this function will cause it to                        return the current list item's value as an int .get_str(*name)        get pointer to string associated with name;                      * the contents of the string are read only; do                        not modify them or you will cause undefined                        behavior                      * this function throws a fatal error if the name                        does not exist; if you are handling optional                        names, use .exists(name) to confirm it exists                        before calling this function .next(void)            in the case of a table, go to next row;                        in a list, go to next item                        returns 0 when there is no next line .remaining(void)       returns non-zero if there are rows (in table),                        items (in list), remaining, 0 otherwise .name(void)            returns string of selected item name                        returns 0 at end of list                        (for use in list types only) .value(void)           returns string of selected item value                        returns 0 at end of list                        (for use in list types only) conf section conf can load files of two formats: "table" and "list" the following information applies to both formats: you can use // to comment out the remainder of a line, or /* to comment out a specific block of text */ the number of tabs or spaces between names/values does not matter, as all contiguous blocks of whitespace are used to determine where one name or value ends and the next begins; the names and values can contain anything but whitespace (you can get around this limitation by putting quotes around them); lastly, keep in mind that the names are NOT cAsE-sEnSiTiVe, and as such, "vram" and "VRAM" are the same the source code provided for each can be executed using zzrtl; just save as .rtl and open in zzrtl; don't forget to make sure their dependencies ("table.txt", "list.txt") are in the same directory as their respective .rtls table notes here is a table that you may load from a file table.txt scene  card  music  notes             fadein 0x02    off   stop  "diligent work"        2 0x03     on     go  "getting ocarina"      3 the first row contains the names that are used to look up the values using the .get_x() functions; the rows that follow contain the values themselves the code for parsing it would look like /****************************************  * <z64.me> zzrtl table parsing example *  ****************************************/ /* table.txt (save in same directory as example) scene  card  music  notes             fadein 0x02    off   stop  "diligent work"        2 0x03     on     go  "getting ocarina"      3 */ int main(int argc, char **argv) {    struct conf *table;    char *card;    char *music;    char *notes;    int  scene;    int  fadein;    /* load the table */    table = table.new("table.txt", "table");    /* now parse all the rows */    while (table.remaining())    {        /* retrieve variables */        scene = table.get_int("scene");        card  = table.get_str("card");        music = table.get_str("music");        fadein = table.get_int("fadein");        notes = table.get_str("notes");              /* do something with the variables here */        printf("scene %d settings:\n", scene);        printf(" > card: %s\n", card);        printf(" > music: %s\n", music);        printf(" > fadein: %d\n", fadein);        printf(" > notes: %s\n", notes);              /* on the first pass, each variable will be whatever the *         * first value row says it should be; on the second pass, *         * it pulls from the next row; there is no third pass    */              /* go to next row */        table.next();    }    /* cleanup */    table.free();    return 0; } list notes here is a list that you may load from a file list.txt vram         0x80800000 unknown      0x01000000 "please do"  "work diligently" //optional     "output changes if you uncomment this line" each row contains a names is used to look up the value to its right using the .get_x() functions; the code for parsing it would look like /***************************************  * <z64.me> zzrtl list parsing example *  ***************************************/ /* list.txt (save in same directory as example) vram         0x80800000 unknown      0x01000000 "please do"  "work diligently" //optional     "output changes if you uncomment this line" */ int main(int argc, char **argv) {    struct conf *list;    char *optional;    char *pleasedo;    int  vram;    int  unknown;    /* load the list */    list = list.new("list.txt", "list");    /* retrieve variables */    vram     = list.get_int("vram");    unknown  = list.get_int("unknown");    pleasedo = list.get_str("please do");      /* how to do optional variables */    if (list.exists("optional"))        optional = list.get_str("optional");    else        optional = 0;    /* do something with the variables here */    printf("vram:         0x%X\n", vram);    printf("unknown:      0x%08X\n", unknown);    printf("'please do': '%s'\n", pleasedo);    if (optional)        printf("optional:    '%s'\n", optional);    /* cleanup */    list.free();    return 0; } migrating a zzromtool project tree to zzrtl first off, if you are taking advantage of zzromtool's built-in table expansion, you are going to need to either:  (a) make everything fit within the original     limits for each table  (b) create your own table expansion mod and     update the sample build script as needed misc, system, shader, patch, repoint.tsv  > this stuff is all deprecated, in favor of modifying   it in the rom directly; should you wish to reimplement   it in your own build script, the option is there  > externalizing some files, like Link's overlay, does   have its benefits; it was left out of the example   build script for the sake of simplicity, but here is   a guide to adding this particular functionality to   the sample build script:   https://github.com/z64me/zzrtl/compare/master...oot-link-cat route.txt  > remove the '#' character from the first row  > any remaining '#' comments should be changed to // scene in the sample build script...  > change "unk_a" to "unk-a:"  > change "unk_b" to "unk-b:"  > change "shader" to "shader:" note:  > save and restrict are not used, but you could add   support for them with some assembly editing and by   updating the build script If you read this far, chances are you don't even need a manual. ;) |