a lot of changes and support languages

This commit is contained in:
Ondřej Novák 2025-02-07 20:26:54 +01:00
parent 185a6e5382
commit f55f92a88b
38 changed files with 1221 additions and 467 deletions

View file

@ -16,6 +16,7 @@ SET(files basicobj.c
strlists.c
cztable.c
music.cpp
string_table.cpp
swaper.c )
add_library(skeldal_libs ${files})

View file

@ -1,120 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include "memman.c"
#include "strlite.c"
TSTR_LIST ls_origin=NULL;
TSTR_LIST ls_new=NULL;
static void load_error_msg(int err,char *filename)
{
switch (err)
{
case -1: puts ("Source or target file not found");break;
case -2: puts ("Unexcepted EOF");break;
case -3: puts ("Internal error in strlite.c");break;
default: printf("Error in string table at line %d.\n",err);
}
printf("File:%s\n",filename);
exit(1);
}
void load_lists(char *filename1,char *filename2)
{
int err;
err=load_string_list(&ls_new,filename1);
if (err) load_error_msg(err,filename1);
err=load_string_list(&ls_origin,filename2);
if (err) load_error_msg(err,filename2);
}
char *create_backup(char *filename)
{
char *c,*d;
c=getmem(strlen(filename)+5);strcpy(c,filename);
d=strrchr(c,'.');if (d==NULL) d=strchr(c,0);
strcpy(d,".bak");
remove(c);
rename(filename,c);
return c;
}
void spoj_stringtable()
{
int i;
int cnt=str_count(ls_new);
for(i=0;i<cnt;i++) if (ls_new[i]!=NULL && ls_origin[i]==NULL)
str_replace(&ls_origin,i,ls_new[i]);
}
static void save_num(FILE *fo,int num)
{
char *c=ls_origin[num];
if (c==NULL) return;
fprintf(fo,"%d ",num);
while (*c) if (*c=='\n') (putc('|',fo),c++);else putc(*c++,fo);
putc('\n',fo);
}
void save_stringtable(char *filename,char *backup_name)
{
FILE *fo,*fb;
int cnt=str_count(ls_origin);
int num,rd;
int oldnum=-1,i;
fb=fopen_icase(backup_name,"rt");
if (fb==NULL)
{
puts("Cannot open backup file for reading.");
exit(1);
}
fo=fopen_icase(filename,"wt");
if (fo==NULL)
{
puts("Cannot open target file for writting.");
exit(1);
}
num=0;
rd=fscanf(fb,"%d",&num);
while (num!=-1)
{
if (rd!=1)
do
{
rd=getc(fb);putc(rd,fo);
}
while(rd!='\n');
else
{
for(i=oldnum+1;i<=num;i++) save_num(fo,i);
while((rd=getc(fb))!=EOF && rd!='\n');
}
oldnum=num;
rd=fscanf(fb,"%d",&num);
}
for(i=oldnum+1;i<cnt;i++) save_num(fo,i);
fprintf(fo,"%d",-1);
fclose(fo);
fclose(fb);
}
void main(int argc,char **argv)
{
char *back;
if (argc!=3)
{
puts("Usage: ADDTEXT source target");
exit(1);
}
load_lists(argv[1],argv[2]);
back=create_backup(argv[2]);
spoj_stringtable();
save_stringtable(argv[2],back);
puts("New texts added...");
}

View file

@ -1,132 +0,0 @@
#include <stdio.h>
#include <io.h>
#include <direct.h>
#include <stdlib.h>
#include <conio.h>
#include <ctype.h>
#include <dos.h>
char copy_path[500];
char *adv_name;
char _change_disk(unsigned znak)
{
unsigned total;
znak-='@';
_dos_setdrive(znak,&total);
_dos_getdrive(&total);
return total==znak;
}
char disk_finder()
{
static struct find_t ft;
int err;
if (!access("SKELDAL.EXE",F_OK) && !access("ADV",F_OK)) return 1;
err=_dos_findfirst("*.*",_A_SUBDIR,&ft);
while (!err)
{
if (ft.attrib & _A_SUBDIR && strcmp(ft.name,".") && strcmp(ft.name,".."))
{
chdir(ft.name);
if (disk_finder()) return 1;
chdir("..");
}
err=_dos_findnext(&ft);
}
return 0;
}
char find_path(char *path)
{SEPARATOR
char *oldpath;
unsigned pismeno='C';
cputs("Hledam...\r");
oldpath=getcwd(NULL,PATH_MAX);
for(pismeno='C';pismeno<='Z';pismeno++)
{
_change_disk(pismeno);
chdir("..");
if (disk_finder()==1)
{
getcwd(path,PATH_MAX);
chdir(oldpath);_change_disk(oldpath[0]);
free(oldpath);
return 1;
}
}
chdir(oldpath);_change_disk(oldpath[0]);
free(oldpath);
return 1;
}
void main(int argc,char **argv)
{
char temp[550];
char rep;
if (argc<2)
{
puts("Nespravne parametry!\n"
"\n"
"Pouziti ADVINST [jmeno]\n"
"\n"
"jmeno = Nazev dobrodruzstvi bez pripony");
return;
}
adv_name=argv[1];
sprintf(temp,"%s.adv",adv_name);
if (access(adv_name,F_OK) && access(temp,F_OK))
{
printf("Nemohu najit zadne dobrodruzstvi s timto jmenem (%s)!\n",adv_name);
return;
}
do
{
printf("Vypiste celou cestu, kde lezi hra (napr: c:\\hry\\skeldal)\n"
"Pokud vlozte otaznik (?), instalator se pokusi hru na disku vyhledat\n"
"Pokud stisknete pouze <ENTER>, instalator se ukonci\n"
">");
gets(copy_path);
if (copy_path[0]=='?')
{
find_path(copy_path);
printf("\nInstalator nasel hru na ceste: %s\n\n",copy_path);
}
if (copy_path[0]==0) return;
sprintf(temp,"%s\\skeldal.exe",copy_path);
rep=access(temp,F_OK);
if (rep) puts("Vami vlozena cesta neni spravna!\n");
else
{
sprintf(temp,"%s\\adv\\%s",copy_path,adv_name);
if (access(temp,F_OK))if (mkdir(temp)!=0) printf("Nedokazal jsem vytvorit adresar %s\n\n\n",temp),rep=1;
}
}
while(rep);
sprintf(temp,"copy %s.adv %s > nul",adv_name,copy_path);
puts(temp);
system(temp);
sprintf(temp,"copy %s\\*.* %s\\adv\\%s > nul",adv_name,copy_path,adv_name);
puts(temp);
system(temp);
sprintf(temp,"%s.bat",adv_name);
if (access(temp,F_OK)==0)
{
sprintf(temp,"%s.bat %s",adv_name,copy_path);
puts(temp);
system(temp);
}
chdir(copy_path);
puts("Instalace uspesna!");
printf("Nove dobrodruzstvi spustis prikazem SKELDAL %s.adv\n",adv_name);
puts("Chces si nyni nove dobrodruzstvi vyzkouset? ANO/NE (cokoliv/N)");
if (toupper(getche())=='N') return;
sprintf(temp,"SKELDAL %s.adv",adv_name);
system(temp);
}

View file

@ -142,6 +142,7 @@ void put_image(const word *image,word *target,int start_line,int sizex,int sizey
void put_picture2picture(const word *source,word *target,int xp,int yp);
//#pragma aux put_picture2picture parm [ESI][EDI][EAX][EDX] modify [ECX]
void draw_rounded_rectangle(int x, int y, int xs, int ys, int radius,
int stroke_color, int fill_color);
#define swap_int(a,b) do {int c=a;a=b;b=c;} while (0);

View file

@ -624,3 +624,160 @@ void rectangle(int x1,int y1,int x2,int y2,int color)
ver_line32(x2,y1,y2);
}
void put_pixel(int x, int y, int c) {
word *addr = getadr32(x, y);
*addr = c;
}
/*--------------------------------------------------------------------------
draw_circle_quarter()
Draws one quarter of a circle of radius 'r' (using Bresenhams algorithm)
centered at (cx, cy). The parameter 'quadrant' selects which quarter:
0 = topleft (draws points in the direction of decreasing x and y)
1 = topright (increasing x, decreasing y)
2 = bottomright (increasing x and y)
3 = bottomleft (decreasing x, increasing y)
The function calls put_pixel() to draw the pixels.
--------------------------------------------------------------------------*/
static void draw_circle_quarter(int cx, int cy, int r, int quadrant, int color) {
int x = 0, y = r;
int d = 3 - 2 * r;
while (x <= y) {
switch (quadrant) {
case 0: /* Top-left quarter */
put_pixel(cx - x, cy - y, color);
put_pixel(cx - y, cy - x, color);
break;
case 1: /* Top-right quarter */
put_pixel(cx + x, cy - y, color);
put_pixel(cx + y, cy - x, color);
break;
case 2: /* Bottom-right quarter */
put_pixel(cx + x, cy + y, color);
put_pixel(cx + y, cy + x, color);
break;
case 3: /* Bottom-left quarter */
put_pixel(cx - x, cy + y, color);
put_pixel(cx - y, cy + x, color);
break;
}
if (d < 0) {
d += 4 * x + 6;
} else {
d += 4 * (x - y) + 10;
y--;
}
x++;
}
}
/*--------------------------------------------------------------------------
draw_rounded_rectangle()
Draws a filled, rounded rectangle with a stroke border. The parameters are:
- (x,y): Topleft coordinate of the bounding rectangle.
- (xs, ys): Width and height.
- radius: The radius of the corner arcs.
- stroke_color: The color of the border.
- fill_color: The color used for filling the interior.
--------------------------------------------------------------------------*/
void draw_rounded_rectangle(int x, int y, int xs, int ys, int radius,
int stroke_color, int fill_color) {
int i, j;
/* Clamp the radius if it is too large */
if (radius * 2 > xs)
radius = xs / 2;
if (radius * 2 > ys)
radius = ys / 2;
int rsq = radius * radius; /* We'll use this to avoid taking square roots */
/* --- Fill the rounded rectangle --- */
for (j = y; j < y + ys; j++) {
for (i = x; i < x + xs; i++) {
int draw_pixel = 1; /* flag: set to 0 if the pixel is outside the filled area */
if (radius > 0) {
/* Top-left corner */
if (i < x + radius && j < y + radius) {
int cx = x + radius;
int cy = y + radius;
int dx = i - cx;
int dy = j - cy;
if (dx * dx + dy * dy > rsq)
draw_pixel = 0;
}
/* Top-right corner */
else if (i >= x + xs - radius && j < y + radius) {
int cx = x + xs - radius - 1;
int cy = y + radius;
int dx = i - cx;
int dy = j - cy;
if (dx * dx + dy * dy > rsq)
draw_pixel = 0;
}
/* Bottom-left corner */
else if (i < x + radius && j >= y + ys - radius) {
int cx = x + radius;
int cy = y + ys - radius - 1;
int dx = i - cx;
int dy = j - cy;
if (dx * dx + dy * dy > rsq)
draw_pixel = 0;
}
/* Bottom-right corner */
else if (i >= x + xs - radius && j >= y + ys - radius) {
int cx = x + xs - radius - 1;
int cy = y + ys - radius - 1;
int dx = i - cx;
int dy = j - cy;
if (dx * dx + dy * dy > rsq)
draw_pixel = 0;
}
}
if (draw_pixel)
put_pixel(i, j, fill_color);
}
}
/* --- Draw the border (stroke) --- */
if (radius > 0) {
/* Draw the top and bottom horizontal lines (excluding the rounded corners) */
for (i = x + radius; i < x + xs - radius; i++) {
put_pixel(i, y, stroke_color); /* Top edge */
put_pixel(i, y + ys - 1, stroke_color); /* Bottom edge */
}
/* Draw the left and right vertical lines (excluding the rounded corners) */
for (j = y + radius; j < y + ys - radius; j++) {
put_pixel(x, j, stroke_color); /* Left edge */
put_pixel(x + xs - 1, j, stroke_color); /* Right edge */
}
/* Draw the four corner arcs */
/* Top-left corner */
draw_circle_quarter(x + radius, y + radius, radius, 0, stroke_color);
/* Top-right corner */
draw_circle_quarter(x + xs - radius - 1, y + radius, radius, 1, stroke_color);
/* Bottom-right corner */
draw_circle_quarter(x + xs - radius - 1, y + ys - radius - 1, radius, 2, stroke_color);
/* Bottom-left corner */
draw_circle_quarter(x + radius, y + ys - radius - 1, radius, 3, stroke_color);
} else {
/* If radius == 0, draw a normal (nonrounded) rectangle border */
for (i = x; i < x + xs; i++) {
put_pixel(i, y, stroke_color);
put_pixel(i, y + ys - 1, stroke_color);
}
for (j = y; j < y + ys; j++) {
put_pixel(x, j, stroke_color);
put_pixel(x + xs - 1, j, stroke_color);
}
}
}

394
libs/csv.hpp Normal file
View file

@ -0,0 +1,394 @@
#pragma once
#include <cstdint>
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
#include <variant>
///Describes mapping of column to a string field in the target structure
/**
* @tparam T type of target structure
*
* Supported types: string, int16-64, unsigned int 16-64, float, double, boolean, char
*
* nullptr is used to skip the field
*/
template<typename T>
struct CSVFieldMapping {
using FieldRef = std::variant<std::string T::*,
std::uint64_t T::*,
std::int64_t T::*,
std::uint32_t T::*,
std::int32_t T::*,
std::uint16_t T::*,
std::int16_t T::*,
char T::*,
unsigned char T::*,
float T::*,
double T::*,
bool T::*,
std::nullptr_t>;
///name of field
std::string name;
///pointer to member field
FieldRef field;
};
///Contains final mapping columns to structure. The instance is created by the function mapColumns
template<typename T>
class CSVFieldIndexMapping : public std::vector<typename CSVFieldMapping<T>::FieldRef > {
public:
using std::vector<typename CSVFieldMapping<T>::FieldRef >::vector;
///contains true, if all fields has been mapped. Otherwise it is false
/** to find, which field is not mapped you need to find it manually
*/
bool allMapped = false;
template<typename X>
constexpr bool isMapped(X T::*ptr) const {
if (allMapped) return true;
return std::find(this->begin(),
this->end(), typename CSVFieldMapping<T>::FieldRef(ptr)) != this->end();
}
};
///Simple CSV reader
/**
* Parses CSV from the source. Each read returns one field until whole CSV is parsed
*
* @tparam Source the type of object which provides source data. This can also be a function.
* because the type must implement following function call <int()>. The input characters
* should be read as unsigned (so characters above 0x80 are not mapped bellow the zero). The
* function must return EOF (-1) as end of file.
*/
template<typename Source>
class CSVReader {
public:
enum class CSVState: int {
///there is next field at the row
next = 0,
///this field is last field at the row
last = 1,
///no more field, eof reached
eof = std::char_traits<char>::eof()
};
///Inicialize the parser
/**
* @param src data source. This is function, which returns next character.
* Returned type should be int.
* If there are no more characters, it should return std::char_traits<char>::eof()
*/
CSVReader(Source &&src)
: _src(std::forward<Source>(src)) {}
///Read next field
/**
* @param buffer reference to a string, which will be filled with content of the next field.
* The string is always cleared at the beginning regardless on how the operation was completed
* @retval next - field read successfully and there is at least one further field
* on the same row
* @retval last - field read successfully and this was the last field on the row,
* next read() will read a field on a new row
* @retval eof - reached end of file, buffer is empty
*/
CSVState read(std::string &buffer);
///Contains eof value
static constexpr auto eof = std::char_traits<char>::eof();
///Reads line and creates mapping of columns to structure
/**
* Function is intended to be called for the first line, which contains columns' names
*
* @tparam T You probably need to explicitly specify type
* @param def definition
* @return mapping
*
*
* @code
* struct CSVCols {
* std::string symbol_data_type;
* std::string format;
* std::string interval;
* std::string directory;
* };
*
* auto mapping = reader.mapColumns<CSVCols>({
* {"symbol_data_type",&CSVCols::symbol_data_type},
* {"format", &CSVCols::format},
* {"interval", &CSVCols::interval},
* {"directory", &CSVCols::directory},
* });
* if (!mapping.allMapped) return false;
* @encode
*/
template<typename T>
CSVFieldIndexMapping<T> mapColumns(const std::initializer_list<CSVFieldMapping<T> > &def);
///Reads row and transfer data to target structure through the mapping object
/**
* @param mapping mapping object created by mapColumns
* @param target target structure which is filled from the line
* @retval true some data read
* @retval false end of table (no data read)
*/
template<typename T>
bool readRow(const CSVFieldIndexMapping<T> &mapping, T &target);
///Reset state - assume that source has been reset
void reset() {
_eof_reached = false;
_beg_line = true;
}
bool skipLine() {
std::string buff;
while (read(buff) == CSVState::next);
return !_eof_reached;
}
///Change the character for quotes
/**
* @param quotes new character for quotes
*
* @note you need to change this value before the first line is read
* (including the function mapColumns())
*
* (default=")
*/
void setQuotes(char quotes) {_quotes = quotes;}
///Retrieves character for quotes
char getQuotes() const {return _quotes;}
///Change the character for field separator
/**
* @param sep new separator
*
* @note you need to change this value before the first line is read
* (including the function mapColumns())
*
* (default=,)
*/
void setSep(char sep) {_sep = sep;}
///set field separator
char getSep() const {return _sep;}
protected:
Source _src;
char _sep = ',';
char _quotes = '"';
bool _eof_reached = false;
bool _beg_line = true;
};
///This CTAD allows to construct CSVReader without template arguments from lambda
/**
* CSVReader csv([&]() -> int {return .... ;});
*/
template<typename Source>
CSVReader(Source) -> CSVReader<Source>;
///Instantiate parser supplying a lambda function as a source
template<typename Fn>
CSVReader<Fn> parseCSV(Fn &&fn) {
return CSVReader<Fn>(std::forward<Fn>(fn));
}
///Instantiate parse supplying an input stream as a source
/**
* @param input input stream. Note it must stay valid during parsing
* @return CSVReader instance
*/
inline auto parseCSVFromFile(std::istream &input) {
return parseCSV([&]{return input.get();});
}
///Instantiate the parser supplying a string as a source
/**
* @param str string source
* @return CSVReader reader
*/
inline auto parseCSVString(std::string &&str) {
return parseCSV([s = std::move(str), pos = std::size_t(0)]() mutable {
return pos>=s.size()?std::char_traits<char>::eof()
:static_cast<int>(static_cast<unsigned char>(s[pos++]));
});
}
///Instantiate the parser supplying a string as a source
/**
* @param str string source - must be valid during parsing
* @return CSVReader reader
*/
inline auto parseCSVString(std::string_view str) {
return parseCSV([=, pos = std::size_t(0)]() mutable {
return pos>=str.size()?std::char_traits<char>::eof()
:static_cast<int>(static_cast<unsigned char>(str[pos++]));
});
}
template<typename Source>
inline typename CSVReader<Source>::CSVState CSVReader<Source>::read(std::string &buffer) {
buffer.clear();
if (_eof_reached) return CSVState::eof;
int c = _src();
if (_beg_line) {
while (c != eof && std::iscntrl(c)) c = _src();
}
if (c == eof) return CSVState::eof;
if (c == _quotes) {
bool loop;
do {
c= _src();
while (c != _quotes && c != eof) {
buffer.push_back(static_cast<char>(c));
c = _src();
}
loop = false;
if (c == _quotes) {
c = _src();
if (c == _quotes) {
loop = true;
buffer.push_back(_quotes);
}
}
} while (loop);
while (c != eof && c != _sep && c != '\n' && c != '\r') {
c = _src(); //ignore incorrect csv content
}
} else {
while (c != _sep && c != eof && c != '\n' && c != '\r') {
buffer.push_back(static_cast<char>(c));
c = _src();
}
}
if (c == eof) _eof_reached = true;
_beg_line = c != _sep;
return _beg_line?CSVState::last:CSVState::next;
}
template<typename Source>
template<typename T>
inline CSVFieldIndexMapping<T> CSVReader<Source>::mapColumns(const std::initializer_list<CSVFieldMapping<T> > &def) {
CSVFieldIndexMapping<T> out;
std::string buff;
std::size_t cnt = def.size();
CSVState st = CSVState::next;
while (st != CSVState::last) {
st = this->read(buff);
if (st == CSVState::eof) break;
auto iter = std::find_if(def.begin(), def.end(), [&](const CSVFieldMapping<T> &x) {
return x.name == buff;
});
if (iter != def.end()) {
out.push_back(iter->field);
cnt--;
} else {
out.push_back(nullptr);
}
}
out.allMapped = cnt == 0;
return out;
}
template<typename Source>
template<typename T>
inline bool CSVReader<Source>::readRow(const CSVFieldIndexMapping<T> &mapping,T &target) {
std::string buff;
std::size_t idx = 0;
CSVState st = CSVState::next;
while (st == CSVState::next) {
if (idx < mapping.size()) {
const auto &m = mapping[idx];
st = std::visit([&](const auto &ptr) -> CSVState{
using TPtr = std::decay_t<decltype(ptr)>;
if constexpr(!std::is_null_pointer_v<TPtr>) {
using TVal = std::decay_t<decltype(target.*ptr)>;
if constexpr(std::is_same_v<TVal, std::string>) {
return read(target.*ptr);
} else if constexpr(std::is_same_v<TVal, std::uint16_t> || std::is_same_v<TVal, std::uint64_t> || std::is_same_v<TVal, std::uint32_t>) {
CSVState st = read(buff);
auto v = std::strtoull(buff.c_str(),nullptr,10);
target.*ptr = static_cast<TVal>(v);
return st;
} else if constexpr(std::is_same_v<TVal, std::int16_t> || std::is_same_v<TVal, std::int64_t> || std::is_same_v<TVal, std::int32_t>) {
CSVState st = read(buff);
auto v = std::strtoll(buff.c_str(),nullptr,10);
target.*ptr = static_cast<TVal>(v);
return st;
} else if constexpr(std::is_same_v<TVal, float> || std::is_same_v<TVal, double>) {
CSVState st = read(buff);
auto v = std::strtod(buff.c_str(), nullptr);
target.*ptr = static_cast<TVal>(v);
return st;
} else if constexpr(std::is_same_v<TVal, char> || std::is_same_v<TVal, unsigned char>) {
CSVState st = read(buff);
if (buff.empty()) target.*ptr = 0;
else target.*ptr = static_cast<TVal>(buff[0]);
return st;
} else {
static_assert(std::is_same_v<TVal, bool>);
CSVState st = read(buff);
std::transform(buff.begin(), buff.end(), buff.begin(), [](char c) { return std::tolower(c); });
if (buff == "y" || buff == "t" || buff =="true" || buff == "yes" || buff == "on") {
target.*ptr = true;
} else if (buff == "n" || buff == "f" || buff =="false" || buff == "no" || buff == "off") {
target.*ptr = false;
} else {
auto v = std::strtod(buff.c_str(), nullptr);
target.*ptr = v != 0;
}
return st;
}
} else {
return read(buff);
}
}, m);
// st = read(target.*m);
if (st == CSVState::eof)
break;
++idx;
} else {
st = read(buff);
}
}
if (st == CSVState::eof && idx == 0) {
return false;
}
//fill-up missing fields
while (idx < mapping.size()) {
const auto &m = mapping[idx];
std::visit([&](const auto &ptr) {
using TPtr = std::decay_t<decltype(ptr)>;
if constexpr(!std::is_null_pointer_v<TPtr>) {
target.*ptr = {};
}
},m);
++idx;
}
return true;
}

51
libs/string_table.cpp Normal file
View file

@ -0,0 +1,51 @@
#include "string_table.h"
#include "csv.hpp"
#include "cztable.h"
#include <unordered_map>
#include <fstream>
#include <memory>
typedef struct stringtable_struct_tag {
std::unordered_map<int, std::string> _strings;
}TSTRINGTABLE;
struct CSVRecord {
int index;
std::string string;
};
TSTRINGTABLE *stringtable_load(const char *filename) {
std::ifstream input(filename);
if (!input) return NULL;
CSVReader reader([&input]{return input.get();});
auto mapping = reader.mapColumns<CSVRecord>({
{"id", &CSVRecord::index},
{"string", &CSVRecord::string},
});
if (!mapping.allMapped) return NULL;
CSVRecord rec;
std::unique_ptr<TSTRINGTABLE> tbl = std::make_unique<TSTRINGTABLE>();
while (reader.readRow(mapping, rec)) {
windows2kamenik(rec.string.data(), rec.string.size(), rec.string.data());
tbl->_strings[rec.index] = rec.string;
}
return tbl.release();
}
const char *stringtable_find(const TSTRINGTABLE *st, int id, const char *default_value) {
if (st) {
auto iter = st->_strings.find(id);
if (iter != st->_strings.end()) {
return iter->second.c_str();
}
}
return default_value;
}
void stringtable_free(TSTRINGTABLE *st) {
delete st;
}

18
libs/string_table.h Normal file
View file

@ -0,0 +1,18 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct stringtable_struct_tag TSTRINGTABLE;
TSTRINGTABLE *stringtable_load(const char *filename);
const char *stringtable_find(const TSTRINGTABLE *st, int id, const char *default_value);
void stringtable_free(TSTRINGTABLE *st);
#ifdef __cplusplus
}
#endif