summaryrefslogtreecommitdiff
path: root/src/gusconf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gusconf.c')
-rw-r--r--src/gusconf.c268
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;
+}
+