diff options
Diffstat (limited to 'src/gusconf.c')
-rw-r--r-- | src/gusconf.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/src/gusconf.c b/src/gusconf.c new file mode 100644 index 00000000..8efd9803 --- /dev/null +++ b/src/gusconf.c @@ -0,0 +1,268 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2013 Simon Howard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +// DESCRIPTION: +// GUS emulation code. +// +// Actually emulating a GUS is far too much work; fortunately +// GUS "emulation" already exists in the form of Timidity, which +// supports GUS patch files. This code therefore converts Doom's +// DMXGUS lump into an equivalent Timidity configuration file. +// +//----------------------------------------------------------------------------- + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "w_wad.h" +#include "z_zone.h" + +#define MAX_INSTRUMENTS 256 + +typedef struct +{ + char *patch_names[MAX_INSTRUMENTS]; + int mapping[MAX_INSTRUMENTS]; +} gus_config_t; + +char *gus_patch_path = ""; +unsigned int gus_ram_kb = 1024; + +static unsigned int MappingIndex(void) +{ + unsigned int result = gus_ram_kb / 256; + + if (result < 1) + { + return 1; + } + else if (result > 4) + { + return 4; + } + else + { + return result; + } +} + +static int SplitLine(char *line, char **fields, unsigned int max_fields) +{ + unsigned int num_fields; + char *p; + + fields[0] = line; + num_fields = 1; + + for (p = line; *p != '\0'; ++p) + { + if (*p == ',') + { + *p = '\0'; + + // Skip spaces following the comma. + do + { + ++p; + } while (*p != '\0' && isspace(*p)); + + fields[num_fields] = p; + ++num_fields; + --p; + + if (num_fields >= max_fields) + { + break; + } + } + else if (*p == '#') + { + *p = '\0'; + break; + } + } + + // Strip off trailing whitespace from the end of the line. + p = fields[num_fields - 1] + strlen(fields[num_fields - 1]); + while (p > fields[num_fields - 1] && isspace(*(p - 1))) + { + --p; + *p = '\0'; + } + + return num_fields; +} + +static void ParseLine(gus_config_t *config, char *line) +{ + char *fields[6]; + unsigned int num_fields; + unsigned int instr_id, mapped_id; + + num_fields = SplitLine(line, fields, 6); + + if (num_fields < 6) + { + return; + } + + instr_id = atoi(fields[0]); + mapped_id = atoi(fields[MappingIndex()]); + + free(config->patch_names[instr_id]); + config->patch_names[instr_id] = strdup(fields[5]); + config->mapping[instr_id] = mapped_id; +} + +static void ParseDMXConfig(char *dmxconf, gus_config_t *config) +{ + char *p, *newline; + unsigned int i; + + memset(config, 0, sizeof(gus_config_t)); + + for (i = 0; i < MAX_INSTRUMENTS; ++i) + { + config->mapping[i] = -1; + } + + p = dmxconf; + + for (;;) + { + newline = strchr(p, '\n'); + + if (newline != NULL) + { + *newline = '\0'; + } + + ParseLine(config, p); + + if (newline == NULL) + { + break; + } + else + { + p = newline + 1; + } + } +} + +static void FreeDMXConfig(gus_config_t *config) +{ + unsigned int i; + + for (i = 0; i < MAX_INSTRUMENTS; ++i) + { + free(config->patch_names[i]); + } +} + +static char *ReadDMXConfig(void) +{ + int lumpnum; + unsigned int len; + char *data; + + // TODO: This should be chosen based on gamemode == commercial: + + lumpnum = W_CheckNumForName("DMXGUS"); + + if (lumpnum < 0) + { + lumpnum = W_GetNumForName("DMXGUSC"); + } + + len = W_LumpLength(lumpnum); + data = Z_Malloc(len + 1, PU_STATIC, NULL); + W_ReadLump(lumpnum, data); + + return data; +} + +static boolean WriteTimidityConfig(char *path, gus_config_t *config) +{ + FILE *fstream; + unsigned int i; + + fstream = fopen(path, "w"); + + if (fstream == NULL) + { + return false; + } + + fprintf(fstream, "# Autogenerated Timidity config.\n\n"); + + fprintf(fstream, "dir %s\n", gus_patch_path); + + fprintf(fstream, "\nbank 0\n\n"); + + for (i = 0; i < 128; ++i) + { + if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS + && config->patch_names[config->mapping[i]] != NULL) + { + fprintf(fstream, "%i %s\n", + i, config->patch_names[config->mapping[i]]); + } + } + + fprintf(fstream, "\ndrumset 0\n\n"); + + for (i = 128 + 25; i < MAX_INSTRUMENTS; ++i) + { + if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS + && config->patch_names[config->mapping[i]] != NULL) + { + fprintf(fstream, "%i %s\n", + i - 128, config->patch_names[config->mapping[i]]); + } + } + + fprintf(fstream, "\n"); + + fclose(fstream); + + return true; +} + +boolean GUS_WriteConfig(char *path) +{ + boolean result; + char *dmxconf; + gus_config_t config; + + dmxconf = ReadDMXConfig(); + ParseDMXConfig(dmxconf, &config); + + result = WriteTimidityConfig(path, &config); + + FreeDMXConfig(&config); + Z_Free(dmxconf); + + return result; +} + |