/*
* 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 .
*/
// InfFile.cpp: implementation of the InfFile class.
//
//////////////////////////////////////////////////////////////////////
#include
#include "cfile.h"
#include "inffile.h"
#include "pstring.h"
#include "pserror.h"
#define INFFILE_NULL (-1024)
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
InfFile::InfFile() { m_fp = nullptr; }
InfFile::~InfFile() {
// close file and free symbol lists
Close();
}
// symbol functions.
void InfFile::AddSymbol(const char *name, const char *text) {
tListNode *symbol = new tListNode;
strcpy(symbol->t.name, name);
symbol->t.text = new char[strlen(text) + 1];
strcpy(symbol->t.text, text);
m_sym_list.link(symbol);
}
void InfFile::FreeSymbols() {
tListNode *sym;
m_sym_list.start();
while ((sym = m_sym_list.start()) != 0) {
sym = m_sym_list.unlink();
if (sym->t.text)
delete[] sym->t.text;
delete sym;
}
}
const char *InfFile::GetSymbolText(const char *name) {
tListNode *sym;
m_sym_list.start();
for (sym = m_sym_list.start(); sym; sym = sym->next) {
if (strcmp(sym->t.name, name) == 0)
return (const char *)sym->t.text;
}
return nullptr;
}
// opens an inf file, pass in a lexical analyzer that will return a command index.
// tag_check is a string that must appear at the very beginning of the file.
// lexfn is a function to match a command to a number. num <= 0 are taken.
bool InfFile::Open(const std::filesystem::path &filename, const char *tag_check, int (*lexfn)(const char *command)) {
if (m_fp)
return false;
m_fp = cfopen(filename, "rt");
if (!m_fp)
return false;
// do tag checking.
if (tag_check) {
cf_ReadString(m_linetext, INFFILE_LINELEN, m_fp);
if (strcmp(tag_check, m_linetext) != 0) {
cfclose(m_fp);
m_fp = NULL;
return false;
}
}
// okay we have a 'valid' inf file.
m_lexfn = lexfn;
m_lineptr = NULL;
m_endptr = NULL;
m_line = 0;
return true;
}
// closes the file
void InfFile::Close() {
if (!m_fp)
return;
FreeSymbols();
m_sym_list.free();
cfclose(m_fp);
m_fp = NULL;
}
// read a command line. returns false on eof.
bool InfFile::ReadLine() {
if (cfeof(m_fp))
return false;
cf_ReadString(m_linetext, INFFILE_LINELEN, m_fp);
// start line.
m_lineptr = &m_linetext[0];
m_endptr = m_lineptr + strlen(m_lineptr) + 1;
m_line++;
return true;
}
// parses a line of 'code'. return value from the lexfn
// a 0 for EOL (end of line).
// -1 for error.
// -2 for comment
int InfFile::ParseLine(char *operand, int oprlen) {
if (!m_lineptr)
return INFFILE_EOL;
// tokenize line.
char command[32]; // Command read from line.
char *opr = NULL, *cmd;
int retval = INFFILE_NULL;
if (strlen(m_lineptr) == 0)
return INFFILE_EOL;
// parse out command and operand (command=str)
// m_lineptr = command, opr = operand, nextcmd = next lineptr.
cmd = strtok(m_lineptr, " \t=:");
if (cmd)
opr = strtok(NULL, ",;");
else
Int3();
// clean out trailing and preceeding trash (tabs, spaces, etc)
CleanupStr(command, cmd, sizeof(command));
if (opr)
CleanupStr(operand, opr, oprlen);
else
operand[0] = 0;
if (strlen(command) == 0) {
if (strlen(operand) == 0)
return INFFILE_EOL;
}
// do predefined commands
if (command[0] == '@')
retval = INFFILE_COMMENT;
// do symbolic commands.
if (operand[0] == '#') {
const char *sym = GetSymbolText(&operand[1]);
if (sym)
strcpy(operand, sym);
else
retval = INFFILE_ERROR;
}
if (retval == INFFILE_NULL) {
if (command[0] == '#') {
AddSymbol(&command[1], operand);
retval = INFFILE_SYMBOL;
} else {
// do command through lex analyzer. if not preprocessed.
if (m_lexfn)
retval = (*m_lexfn)(command);
}
}
// adjust text pointers to prepare for next read
char *lastptr = (opr) ? opr : cmd;
m_lineptr = lastptr + strlen(lastptr) + 1;
if (m_lineptr >= m_endptr)
m_lineptr = NULL;
// end
return retval;
}