mirror of
https://github.com/ondra-novak/gates_of_skeldal.git
synced 2025-07-18 20:26:45 -04:00
a lot of changes and support languages
This commit is contained in:
parent
185a6e5382
commit
f55f92a88b
38 changed files with 1221 additions and 467 deletions
|
@ -16,6 +16,7 @@ SET(files basicobj.c
|
|||
strlists.c
|
||||
cztable.c
|
||||
music.cpp
|
||||
string_table.cpp
|
||||
swaper.c )
|
||||
|
||||
add_library(skeldal_libs ${files})
|
||||
|
|
120
libs/addtext.c
120
libs/addtext.c
|
@ -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...");
|
||||
}
|
132
libs/advinst.c
132
libs/advinst.c
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
157
libs/bgraph2.c
157
libs/bgraph2.c
|
@ -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 Bresenham’s algorithm)
|
||||
centered at (cx, cy). The parameter 'quadrant' selects which quarter:
|
||||
0 = top–left (draws points in the direction of decreasing x and y)
|
||||
1 = top–right (increasing x, decreasing y)
|
||||
2 = bottom–right (increasing x and y)
|
||||
3 = bottom–left (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): Top–left 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 (non–rounded) 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
394
libs/csv.hpp
Normal 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
51
libs/string_table.cpp
Normal 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
18
libs/string_table.h
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue