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;
}