mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 19:55:23 +00:00
382 lines
11 KiB
C++
382 lines
11 KiB
C++
/*
|
|
* Descent 3
|
|
* Copyright (C) 2024 Parallax Software
|
|
*
|
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
--- HISTORICAL COMMENTS FOLLOW ---
|
|
|
|
* $Logfile: /DescentIII/Main/music/omflex.cpp $
|
|
* $Revision: 14 $
|
|
* $Date: 4/01/99 4:50p $
|
|
* $Author: Matt $
|
|
*
|
|
* Read OMF file lexical analyzer.
|
|
*
|
|
* $Log: /DescentIII/Main/music/omflex.cpp $
|
|
*
|
|
* 14 4/01/99 4:50p Matt
|
|
* Took out Warning() function, chaning those calls to mprintf()
|
|
*
|
|
* 13 2/27/99 6:52p Samir
|
|
* fixed label-region scope problem.
|
|
*
|
|
* 12 1/28/99 2:22p Samir
|
|
* simplified music system for D3.
|
|
*
|
|
* 11 12/15/98 10:22a Samir
|
|
* upped max labels.
|
|
*
|
|
* 10 12/11/98 3:28p Samir
|
|
* added start of regions
|
|
*
|
|
* 9 12/08/98 11:41a Samir
|
|
* added new region commands.
|
|
*
|
|
* 8 12/07/98 2:57p Samir
|
|
* added transitions for combat.
|
|
*
|
|
* 7 12/03/98 12:48p Samir
|
|
* speed up sequencer so that gap between samples isn't too big.
|
|
*
|
|
* 6 11/20/98 5:20p Samir
|
|
* added a bunch of high level scripting commands.
|
|
*
|
|
* 5 11/16/98 4:15p Samir
|
|
* added death.
|
|
*
|
|
* 4 11/13/98 2:27p Samir
|
|
* new music system.
|
|
*
|
|
* 3 8/10/98 5:56p Samir
|
|
* new command for end of combat music added.
|
|
*
|
|
* 2 7/28/98 5:43p Samir
|
|
* reorg of music system.
|
|
*
|
|
* 1 7/28/98 11:48a Samir
|
|
* moved from sequencer.cpp
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
|
|
#include "music.h"
|
|
#include "musiclib.h"
|
|
#include "inffile.h"
|
|
#include "mem.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
// OMF INF FILE READ
|
|
#define OMFFILEERR_ADDSECTION INFFILE_CUSTOM // error adding section to list.
|
|
#define OMFFILEERR_INSOVERFLOW (INFFILE_CUSTOM + 1) // too many instructions!
|
|
#define OMFFILEERR_LBLOVERFLOW (INFFILE_CUSTOM + 2) // too many labels!
|
|
#define OMFFILEERR_GOTO (INFFILE_CUSTOM + 3) // no goto label exists
|
|
#define OMFFILEERR_SYNTAX (INFFILE_CUSTOM + 4) // syntax error.
|
|
|
|
static const char *OMFCommands[OMFCMD_NUM] = {
|
|
"stream", "section", "play", "endsection", "label", "compare", "lcmp", "llpt", "lplc", "inci", "seti",
|
|
"ifi", "region", "endregion", "q2", "blt", "bgt", "beq", "bnif", "goto", "wait"};
|
|
static int OMFLex(const char *command);
|
|
|
|
int OMFLex(const char *command) {
|
|
for (int i = 0; i < OMFCMD_NUM; i++) {
|
|
if (strcmp(OMFCommands[i], command) == 0)
|
|
return i;
|
|
}
|
|
|
|
return INFFILE_ERROR;
|
|
}
|
|
|
|
#define ADD_NEW_INS_NUM(_cmd, _num) \
|
|
do { \
|
|
if (temp_ins_idx == MAX_MUSIC_INSTRUCTIONS) { \
|
|
cmd = OMFFILEERR_INSOVERFLOW; \
|
|
goto force_error; \
|
|
} \
|
|
temp_ins_buf[temp_ins_idx].cmd = (_cmd); \
|
|
temp_ins_buf[temp_ins_idx++].opr.num = (_num); \
|
|
} while (0)
|
|
|
|
#define ADD_NEW_INS_STR(_cmd, _str) \
|
|
do { \
|
|
if (temp_ins_idx == MAX_MUSIC_INSTRUCTIONS) { \
|
|
cmd = OMFFILEERR_INSOVERFLOW; \
|
|
goto force_error; \
|
|
} \
|
|
temp_ins_buf[temp_ins_idx].cmd = (_cmd); \
|
|
temp_ins_buf[temp_ins_idx++].opr.str = alloc_and_copy_str((_str)); \
|
|
} while (0)
|
|
|
|
// takes a filename containing oms data.
|
|
bool OutrageMusicSeq::LoadTheme(const char *file) {
|
|
const int MAX_FILE_LABELS = 256;
|
|
|
|
InfFile inf;
|
|
char operand[INFFILE_LINELEN]; // operand
|
|
int theme_type;
|
|
music_ins temp_ins_buf[MAX_MUSIC_INSTRUCTIONS];
|
|
int temp_ins_idx = 0;
|
|
int16_t cur_region;
|
|
struct {
|
|
char *name;
|
|
int16_t index;
|
|
int16_t region;
|
|
} labels[MAX_FILE_LABELS];
|
|
int n_labels = 0, n_relabels = 0;
|
|
bool in_region = false;
|
|
|
|
// reset list management for new theme.
|
|
m_tracklist.reset();
|
|
m_ins_curptr = m_ins_buffer;
|
|
m_str_curptr = m_str_buffer;
|
|
|
|
// open file
|
|
if (!file)
|
|
return false;
|
|
|
|
if (!inf.Open(file, "[theme file]", OMFLex)) {
|
|
mprintf(0, "Unable to find requested theme %s or bad file.\n", file);
|
|
return false;
|
|
}
|
|
|
|
cur_region = 0;
|
|
|
|
// perform FIRST PASS for LABELS
|
|
while (inf.ReadLine()) {
|
|
int cmd;
|
|
bool ifi_block;
|
|
ifi_block = false;
|
|
while ((cmd = inf.ParseLine(operand, INFFILE_LINELEN)) > INFFILE_ERROR) {
|
|
switch (cmd) {
|
|
case OMFCMD_LABEL:
|
|
if (n_labels == MAX_FILE_LABELS) {
|
|
cmd = OMFFILEERR_LBLOVERFLOW;
|
|
goto force_pre_error;
|
|
}
|
|
labels[n_labels].index = temp_ins_idx;
|
|
labels[n_labels].region = cur_region;
|
|
labels[n_labels++].name = mem_strdup(operand);
|
|
break;
|
|
case OMFCMD_SECTION:
|
|
case OMFCMD_REGION:
|
|
temp_ins_idx = 0;
|
|
break;
|
|
case OMFCMD_ENDREGION:
|
|
cur_region++;
|
|
break;
|
|
case OMFCMD_STREAM:
|
|
break;
|
|
case OMFCMD_BNIF:
|
|
case OMFCMD_BLT:
|
|
case OMFCMD_BGT:
|
|
case OMFCMD_BEQ:
|
|
case OMFCMD_GOTO:
|
|
case OMFCMD_ENDSECTION:
|
|
case OMFCMD_WAIT:
|
|
case OMFCMD_COMPARE:
|
|
case OMFCMD_LLPT:
|
|
case OMFCMD_SETI:
|
|
case OMFCMD_LCMP:
|
|
case OMFCMD_LPLC:
|
|
case OMFCMD_INCI:
|
|
temp_ins_idx++;
|
|
break;
|
|
case OMFCMD_IFI:
|
|
temp_ins_idx++;
|
|
ifi_block = true;
|
|
break;
|
|
case OMFCMD_PLAY:
|
|
temp_ins_idx++;
|
|
temp_ins_idx++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ifi_block) {
|
|
temp_ins_idx++;
|
|
}
|
|
force_pre_error:
|
|
if (cmd == INFFILE_ERROR) {
|
|
mprintf(0, "Error in music file %s line %d.\n", file, inf.line());
|
|
Int3();
|
|
} else if (cmd == OMFFILEERR_LBLOVERFLOW) {
|
|
mprintf(0, "Error in music file %s line %d (too many labels).\n", file, inf.line());
|
|
Int3();
|
|
}
|
|
}
|
|
|
|
inf.Close();
|
|
|
|
// reopen for SECOND PASS (actual code read)
|
|
if (!inf.Open(file, "[theme file]", OMFLex)) {
|
|
mprintf(0, "Unable to find requested theme %s or bad file.\n", file);
|
|
return false;
|
|
}
|
|
|
|
theme_type = -1;
|
|
cur_region = 0;
|
|
|
|
while (inf.ReadLine()) {
|
|
int cmd, i;
|
|
bool ifi_block;
|
|
ifi_block = false;
|
|
|
|
while ((cmd = inf.ParseLine(operand, INFFILE_LINELEN)) > INFFILE_ERROR) {
|
|
switch (cmd) {
|
|
case OMFCMD_REGION:
|
|
if (in_region) {
|
|
cmd = OMFFILEERR_SYNTAX;
|
|
goto force_error;
|
|
}
|
|
temp_ins_idx = 0;
|
|
in_region = true;
|
|
break;
|
|
|
|
case OMFCMD_ENDREGION:
|
|
in_region = false;
|
|
cur_region++;
|
|
break;
|
|
|
|
case OMFCMD_STREAM: {
|
|
char *sym = strchr(operand, '$'); // symbol AFTER '$'
|
|
if (!sym) {
|
|
cmd = INFFILE_ERROR;
|
|
goto force_error;
|
|
}
|
|
sym[0] = 0;
|
|
m_tracklist.add(operand, &sym[1]); // sym[0] was '$' now null terminator
|
|
} break;
|
|
|
|
case OMFCMD_LABEL:
|
|
// already taken care of.
|
|
ASSERT(labels[n_relabels].index == temp_ins_idx);
|
|
n_relabels++;
|
|
break;
|
|
|
|
case OMFCMD_BNIF:
|
|
case OMFCMD_BLT:
|
|
case OMFCMD_BGT:
|
|
case OMFCMD_BEQ:
|
|
case OMFCMD_GOTO:
|
|
for (i = 0; i < n_labels; i++) {
|
|
if (labels[i].region == cur_region && strcmp(operand, labels[i].name) == 0) {
|
|
ADD_NEW_INS_NUM(cmd, labels[i].index);
|
|
break;
|
|
}
|
|
}
|
|
if (i == n_labels) {
|
|
cmd = OMFFILEERR_GOTO;
|
|
goto force_error;
|
|
}
|
|
break;
|
|
|
|
case OMFCMD_SECTION:
|
|
if (!in_region) {
|
|
cmd = OMFFILEERR_SYNTAX;
|
|
goto force_error;
|
|
}
|
|
if (strcmp(operand, "intro") == 0) {
|
|
theme_type = OMS_THEME_TYPE_INTRO;
|
|
} else if (strcmp(operand, "idle") == 0) {
|
|
theme_type = OMS_THEME_TYPE_IDLE;
|
|
} else if (strcmp(operand, "combat") == 0) {
|
|
theme_type = OMS_THEME_TYPE_COMBAT;
|
|
} else if (strcmp(operand, "death") == 0) {
|
|
theme_type = OMS_THEME_TYPE_DEATH;
|
|
} else if (strcmp(operand, "idle_combat") == 0) {
|
|
theme_type = OMS_THEME_TYPE_IDLE_TO_COMBAT;
|
|
} else if (strcmp(operand, "combat_idle") == 0) {
|
|
theme_type = OMS_THEME_TYPE_COMBAT_TO_IDLE;
|
|
}
|
|
|
|
temp_ins_idx = 0; // reset instruction buffer
|
|
break;
|
|
|
|
case OMFCMD_ENDSECTION:
|
|
ADD_NEW_INS_NUM(OMFCMD_ENDSECTION, 0);
|
|
if (!AddToList((int16_t)cur_region, (int16_t)theme_type, temp_ins_idx, temp_ins_buf)) {
|
|
cmd = OMFFILEERR_ADDSECTION;
|
|
goto force_error;
|
|
}
|
|
theme_type = -1;
|
|
break;
|
|
|
|
case OMFCMD_PLAY:
|
|
ADD_NEW_INS_NUM(OMFCMD_PLAY, 0);
|
|
ADD_NEW_INS_NUM(OMFCMD_MPLAY, 0);
|
|
break;
|
|
|
|
case OMFCMD_WAIT:
|
|
ADD_NEW_INS_NUM(OMFCMD_WAIT, strcmp(operand, "finished") ? 0 : 1);
|
|
break;
|
|
|
|
case OMFCMD_COMPARE: {
|
|
tMusicVal val;
|
|
sscanf(operand, "r%hd", &val);
|
|
ADD_NEW_INS_NUM(OMFCMD_COMPARE, val);
|
|
break;
|
|
}
|
|
|
|
case OMFCMD_LLPT:
|
|
ADD_NEW_INS_STR(OMFCMD_LLPT, operand);
|
|
break;
|
|
|
|
case OMFCMD_SETI:
|
|
case OMFCMD_LCMP:
|
|
case OMFCMD_LPLC:
|
|
ADD_NEW_INS_NUM(cmd, (tMusicVal)atoi(operand));
|
|
break;
|
|
|
|
case OMFCMD_INCI:
|
|
ADD_NEW_INS_NUM(cmd, 0);
|
|
break;
|
|
|
|
case OMFCMD_IFI:
|
|
ADD_NEW_INS_NUM(cmd, (tMusicVal)atoi(operand));
|
|
ifi_block = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ifi_block) {
|
|
ADD_NEW_INS_NUM(OMFCMD_ENDIFI, 0);
|
|
}
|
|
|
|
force_error:
|
|
if (cmd == INFFILE_ERROR) {
|
|
mprintf(0, "Error in music file %s line %d.\n", file, inf.line());
|
|
Int3();
|
|
} else if (cmd == OMFFILEERR_ADDSECTION) {
|
|
mprintf(0, "Error in music file %s line %d (failed to add section).\n", file, inf.line());
|
|
Int3();
|
|
} else if (cmd == OMFFILEERR_INSOVERFLOW) {
|
|
mprintf(0, "Error in music file %s line %d (too many instructions).\n", file, inf.line());
|
|
Int3();
|
|
}
|
|
}
|
|
|
|
// free any memory
|
|
while (n_labels) {
|
|
mem_free(labels[n_labels - 1].name);
|
|
n_labels--;
|
|
}
|
|
|
|
// close file
|
|
inf.Close();
|
|
|
|
return true;
|
|
}
|