mkznd - KVonGit/zil-stuff GitHub Wiki

Converts input file to a SND file to use in Frotz on DOS.

Input file must be: RAW mono 16,000 Hz 8-bit unsigned integer

mkznd.c

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

// Infocom Sound File Header Structure
// This is the same structure you identified earlier.
typedef struct {
    uint16_t Header;        // Value is typically 22
    uint8_t RepeatsToPlay;  // Repeats, 1 = play once
    uint8_t BaseNote;       // Not used for digital audio (set to 60)
    uint16_t SampleFreq;    // Sampling frequency, e.g., 16000
    uint16_t Unused;        // Unused (set to 0)
    uint16_t DataLength;    // Length of the audio data in bytes
} InfocomSndHeader;

void usage(const char *progname) {
    fprintf(stderr, "%s created by KV & Google Gemini 2.5, 2025-AUG-08\n", progname);
    fprintf(stderr, "Usage: %s <input.raw>\n", progname);
    fprintf(stderr, "[NOTE: The input file should be RAW mono 16000Hz 8-bit unsigned.]\n");
    exit(1);
}

// Function to perform a simple byte swap for 16-bit values
uint16_t swap_uint16(uint16_t val) {
    return (val << 8) | (val >> 8);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        usage(argv[0]);
    }

    const char *input_path = argv[1];

    FILE *fin = fopen(input_path, "rb");
    if (!fin) {
        perror("Error opening input file");
        return 1;
    }

    // --- AUTOMATIC OUTPUT FILENAME GENERATION ---
    char output_path[256];
    strncpy(output_path, input_path, sizeof(output_path) - 1);
    output_path[sizeof(output_path) - 1] = '\0';
    
    char *dot = strrchr(output_path, '.');
    if (dot != NULL) {
        strcpy(dot, ".snd");
    } else {
        strcat(output_path, ".snd");
    }
    // --- END FILENAME GENERATION ---

    // Get the size of the raw audio data
    if (fseek(fin, 0, SEEK_END) != 0) {
        perror("fseek failed");
        fclose(fin);
        return 1;
    }
    long raw_data_length = ftell(fin);
    if (raw_data_length < 0) {
        perror("ftell failed");
        fclose(fin);
        return 1;
    }
    rewind(fin);

    // Create the header
    InfocomSndHeader header;
    header.Header = 22;
    header.RepeatsToPlay = 1;
    header.BaseNote = 60;
    header.SampleFreq = 16000;
    header.Unused = 0;
    header.DataLength = (uint16_t)raw_data_length;
    
    // Infocom files use big-endian byte order, so swap if on a little-endian machine
    uint16_t test_endian = 1;
    if (*((char*)&test_endian) == 1) {
        header.Header = swap_uint16(header.Header);
        header.SampleFreq = swap_uint16(header.SampleFreq);
        header.Unused = swap_uint16(header.Unused);
        header.DataLength = swap_uint16(header.DataLength);
    }

    FILE *fout = fopen(output_path, "wb");
    if (!fout) {
        perror("Error opening output file");
        fclose(fin);
        return 1;
    }

    // Write the header to the output file
    fwrite(&header, sizeof(InfocomSndHeader), 1, fout);

    // Copy the raw audio data from input to output
    uint8_t buffer[1024];
    size_t bytes_read;
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), fin)) > 0) {
        fwrite(buffer, 1, bytes_read, fout);
    }
    
    fclose(fin);
    fclose(fout);

    printf("Successfully created Infocom sound file: %s\n", output_path);
    printf("  - Data Length: %ld bytes\n", raw_data_length);
    printf("  - Total File Size: %ld bytes\n", (long)sizeof(InfocomSndHeader) + raw_data_length);

    return 0;
}
⚠️ **GitHub.com Fallback** ⚠️