WinUAE/ini.cpp
2023-03-26 21:45:58 +03:00

583 lines
13 KiB
C++

#include "sysconfig.h"
#include "sysdeps.h"
#include "ini.h"
static TCHAR *initrim(TCHAR *s)
{
while (*s != 0 && *s <= 32)
s++;
TCHAR *s2 = s;
while (*s2)
s2++;
while (s2 > s) {
s2--;
if (*s2 > 32)
break;
*s2 = 0;
}
return s;
}
void ini_free(struct ini_data *ini)
{
if (!ini)
return;
for(int c = 0; c < ini->inilines; c++) {
struct ini_line *il = ini->inidata[c];
if (il) {
xfree(il->section);
xfree(il->key);
xfree(il->value);
xfree(il);
}
ini->inidata[c] = NULL;
}
xfree(ini);
}
static void ini_sort(struct ini_data *ini)
{
for(int c1 = 0; c1 < ini->inilines; c1++) {
struct ini_line *il1 = ini->inidata[c1];
if (il1 == NULL)
continue;
for (int c2 = c1 + 1; c2 < ini->inilines; c2++) {
struct ini_line *il2 = ini->inidata[c2];
if (il2 == NULL)
continue;
int order = 0;
int sec = _tcsicmp(il1->section, il2->section);
if (sec) {
if (!il1->section_order && !il2->section_order)
order = sec;
else
order = il2->section_order - il1->section_order;
} else {
order = _tcsicmp(il1->key, il2->key);
}
if (order > 0) {
struct ini_line il;
memcpy(&il, il1, sizeof(struct ini_line));
memcpy(il1, il2, sizeof(struct ini_line));
memcpy(il2, &il, sizeof(struct ini_line));
}
}
}
#if 0
for(int c1 = 0; c1 < inilines; c1++) {
struct ini_line *il1 = inidata[c1];
if (il1)
write_log(_T("[%s] %s %s\n"), il1->section, il1->key, il1->value);
}
write_log(_T("\n"));
#endif
}
void ini_addnewcomment(struct ini_data *ini, const TCHAR *section, const TCHAR *val)
{
ini_addnewstring(ini, section, _T(""), val);
}
void ini_addnewstring(struct ini_data *ini, const TCHAR *section, const TCHAR *key, const TCHAR *val)
{
struct ini_line *il = xcalloc(struct ini_line, 1);
if (!il)
return;
il->section = my_strdup(section);
if (!_tcsicmp(section, _T("WinUAE")))
il->section_order = 1;
if (key == NULL) {
il->key = my_strdup(_T(""));
il->value = my_strdup(_T(""));
} else {
il->key = my_strdup(key);
il->value = my_strdup(val);
}
int cnt = 0;
while (cnt < ini->inilines && ini->inidata[cnt])
cnt++;
if (cnt == ini->inilines) {
ini->inilines += 10;
ini->inidata = xrealloc(struct ini_line*, ini->inidata, ini->inilines);
if (!ini->inidata) {
xfree(il->key);
xfree(il->value);
xfree(il);
return;
}
int cnt2 = cnt;
while (cnt2 < ini->inilines) {
ini->inidata[cnt2++] = NULL;
}
}
ini->inidata[cnt] = il;
ini->modified = true;
}
void ini_addnewval(struct ini_data *ini, const TCHAR *section, const TCHAR *key, uae_u32 v)
{
TCHAR tmp[MAX_DPATH];
_stprintf(tmp, _T("%08X ; %u"), v, v);
ini_addnewstring(ini, section, key, tmp);
}
void ini_addnewval64(struct ini_data *ini, const TCHAR *section, const TCHAR *key, uae_u64 v)
{
TCHAR tmp[MAX_DPATH];
_stprintf(tmp, _T("%016llX ; %llu"), v, v);
ini_addnewstring(ini, section, key, tmp);
}
void ini_addnewdata(struct ini_data *ini, const TCHAR *section, const TCHAR *key, const uae_u8 *data, int len)
{
TCHAR *s = xcalloc(TCHAR, len * 3);
if (!s)
return;
_tcscpy(s, _T("\\\n"));
int w = 32;
for (int i = 0; i < len; i += w) {
if (i > 0)
_tcscat(s, _T(" \\\n"));
TCHAR *p = s + _tcslen(s);
for (int j = 0; j < w && j + i < len; j++) {
_stprintf (p, _T("%02X"), data[i + j]);
p += 2;
}
*p = 0;
}
ini_addnewstring(ini, section, key, s);
xfree(s);
}
static const uae_u8 bom[3] = { 0xef, 0xbb, 0xbf };
struct ini_data *ini_new(void)
{
struct ini_data *iniout = xcalloc(ini_data, 1);
return iniout;
}
struct ini_data *ini_load(const TCHAR *path, bool sort)
{
bool utf8 = false;
TCHAR section[MAX_DPATH];
uae_u8 tmp[3];
struct ini_data ini = { 0 };
int section_id = 1;
if (path == NULL || path[0] == 0)
return NULL;
FILE *f = _tfopen(path, _T("rb"));
if (!f)
return NULL;
size_t v = fread(tmp, 1, sizeof tmp, f);
fclose (f);
if (v == 3 && tmp[0] == 0xef && tmp[1] == 0xbb && tmp[2] == 0xbf) {
f = _tfopen (path, _T("rt, ccs=UTF-8"));
} else {
f = _tfopen (path, _T("rt"));
}
section[0] = 0;
for (;;) {
TCHAR tbuffer[MAX_DPATH];
tbuffer[0] = 0;
if (!fgetws(tbuffer, MAX_DPATH, f))
break;
TCHAR *s = initrim(tbuffer);
if (_tcslen(s) < 3)
continue;
if (s[0] == ';')
continue;
if (s[0] == '[' && s[_tcslen(s) - 1] == ']') {
s[_tcslen(s) - 1] = 0;
_tcscpy(section, s + 1);
for (int c = 0; c < ini.inilines; c++) {
struct ini_line *il = ini.inidata[c];
if (il && !_tcscmp(il->section, section)) {
section_id++;
_stprintf(section + _tcslen(section), _T("|%d"), section_id);
break;
}
}
continue;
}
if (section[0] == 0)
continue;
TCHAR *s1 = _tcschr(s, '=');
if (s1) {
*s1++ = 0;
TCHAR *s2 = my_strdup(initrim(tbuffer));
TCHAR *s3 = initrim(s1);
if (s3[0] == '\\' && s3[1] == 0) {
// multiline
xfree(s3);
s3 = NULL;
int len = MAX_DPATH;
TCHAR *otxt = xcalloc(TCHAR, len);
for (;;) {
tbuffer[0] = 0;
if (!fgetws(tbuffer, MAX_DPATH, f))
break;
s3 = initrim(tbuffer);
if (s3[0] == 0)
break;
bool nl = s3[_tcslen(s3) - 1] == '\\';
if (nl) {
s3[_tcslen(s3) - 1] = 0;
initrim(s3);
}
if (_tcslen(otxt) + _tcslen(s3) + 1 >= len) {
len += MAX_DPATH;
otxt = xrealloc(TCHAR, otxt, len);
}
_tcscat(otxt, s3);
s3 = NULL;
if (!nl)
break;
}
xfree(s3);
ini_addnewstring(&ini, section, s2, otxt);
} else {
ini_addnewstring(&ini, section, s2, s3);
}
xfree(s2);
}
}
fclose(f);
if (sort)
ini_sort(&ini);
struct ini_data *iniout = xcalloc(ini_data, 1);
if (iniout) {
memcpy(iniout, &ini, sizeof(struct ini_data));
iniout->modified = false;
}
return iniout;
}
bool ini_save(struct ini_data *ini, const TCHAR *path)
{
TCHAR section[MAX_DPATH];
TCHAR sep[2] = { '=', 0 };
TCHAR com[3] = { ';', ' ', 0 };
TCHAR lf[2] = { 10, 0 };
TCHAR left[2] = { '[', 0 };
TCHAR right[2] = { ']', 0 };
if (!ini)
return false;
ini_sort(ini);
FILE *f = _tfopen(path, _T("wt, ccs=UTF-8"));
if (!f)
return false;
section[0] = 0;
for (int c = 0; c < ini->inilines; c++) {
TCHAR out[MAX_DPATH];
struct ini_line *il = ini->inidata[c];
if (!il)
continue;
if (_tcscmp(il->section, section)) {
_tcscpy(out, lf);
_tcscat(out, left);
_tcscat(out, il->section);
_tcscat(out, right);
_tcscat(out, lf);
fputws(out, f);
_tcscpy(section, il->section);
}
if (il->key[0] != 0 || il->value[0] != 0) {
if (il->key[0] == 0) {
fputws(com, f);
} else {
fputws(il->key, f);
fputws(sep, f);
}
fputws(il->value, f);
fputws(lf, f);
}
}
fclose(f);
ini->modified = false;
return true;
}
bool ini_nextsection(struct ini_data *ini, TCHAR *section)
{
if (!ini)
return false;
TCHAR nextsect[256];
_tcscpy(nextsect, section);
const TCHAR *s = _tcschr(nextsect, '|');
if (s) {
int sectionid = _tstol(s + 1);
_stprintf(nextsect + (s - nextsect) + 1, _T("%d"), sectionid + 1);
} else {
_tcscpy(nextsect + _tcslen(nextsect), _T("|2"));
}
for (int c = 0; c < ini->inilines; c++) {
struct ini_line *il = ini->inidata[c];
if (il && !_tcsicmp(section, il->section)) {
for (int c2 = c + 1; c2 < ini->inilines; c2++) {
il = ini->inidata[c2];
if (il && !_tcsicmp(nextsect, il->section)) {
_tcscpy(section, nextsect);
return true;
}
}
return false;
}
}
return false;
}
bool ini_getstring_multi(struct ini_data *ini, const TCHAR *section, const TCHAR *key, TCHAR **out, struct ini_context *ctx)
{
if (!ini)
return false;
int start = ctx ? ctx->start : 0;
int end = ctx ? (ini->inilines > ctx->end ? ctx->end : ini->inilines) : ini->inilines;
for (int c = start; c < end; c++) {
struct ini_line *il = ini->inidata[c];
if (il && !_tcsicmp(section, il->section) && (key == NULL || !_tcsicmp(key, il->key))) {
if (out) {
*out = my_strdup(il->value);
}
if (ctx)
ctx->lastpos = c;
return true;
}
}
return false;
}
bool ini_getstring(struct ini_data *ini, const TCHAR *section, const TCHAR *key, TCHAR **out)
{
return ini_getstring_multi(ini, section, key, out, NULL);
}
bool ini_getval_multi(struct ini_data *ini, const TCHAR *section, const TCHAR *key, int *v, struct ini_context *ctx)
{
TCHAR *out2 = NULL;
if (!ini_getstring_multi(ini, section, key, &out2, ctx))
return false;
if (out2[0] == 0)
return false;
if (_tcslen(out2) > 2 && out2[0] == '0' && _totupper(out2[1]) == 'X') {
TCHAR *endptr;
*v = _tcstol(out2 + 2, &endptr, 16);
} else {
*v = _tstol(out2);
}
xfree(out2);
return true;
}
bool ini_getval(struct ini_data *ini, const TCHAR *section, const TCHAR *key, int *v)
{
return ini_getval_multi(ini, section, key, v, NULL);
}
bool ini_getbool(struct ini_data *ini, const TCHAR *section, const TCHAR *key, bool *v)
{
TCHAR *s = NULL;
if (!ini_getstring(ini, section, key, &s))
return false;
if (!_tcsicmp(s, _T("true")) || !_tcsicmp(s, _T("1"))) {
xfree(s);
*v = true;
return true;
}
if (!_tcsicmp(s, _T("false")) || !_tcsicmp(s, _T("0"))) {
xfree(s);
*v = false;
return true;
}
return false;
}
bool ini_getdata_multi(struct ini_data *ini, const TCHAR *section, const TCHAR *key, uae_u8 **out, int *size, struct ini_context *ctx)
{
TCHAR *out2 = NULL;
uae_u8 *outp = NULL;
int len;
bool quoted = false;
int j = 0;
if (!ini_getstring_multi(ini, section, key, &out2, ctx))
return false;
len = uaetcslen(out2);
outp = xcalloc(uae_u8, len);
if (!outp)
goto err;
for (int i = 0; i < len; ) {
TCHAR c1 = _totupper(out2[i + 0]);
if (c1 == '\"') {
quoted = !quoted;
i++;
} else {
if (quoted) {
outp[j++] = (uae_u8)c1;
i++;
} else {
TCHAR c2 = _totupper(out2[i + 1]);
if (c1 > 0 && c1 <= ' ') {
i++;
continue;
}
if (i + 1 >= len)
goto err;
if (c1 >= 'A')
c1 -= 'A' - 10;
else if (c1 >= '0')
c1 -= '0';
if (c1 > 15)
goto err;
if (c2 >= 'A')
c2 -= 'A' - 10;
else if (c2 >= '0')
c2 -= '0';
if (c2 > 15)
goto err;
outp[j++] = c1 * 16 + c2;
i += 2;
}
}
}
if (quoted)
goto err;
*out = outp;
*size = j;
return true;
err:
xfree(out2);
xfree(outp);
return false;
}
bool ini_getdata(struct ini_data *ini, const TCHAR *section, const TCHAR *key, uae_u8 **out, int *size)
{
return ini_getdata_multi(ini, section, key, out, size, NULL);
}
bool ini_getsection(struct ini_data *ini, int idx, TCHAR **section)
{
const TCHAR *sptr = NULL;
for (int c = 0; c < ini->inilines; c++) {
struct ini_line *il = ini->inidata[c];
if (il) {
if (!sptr) {
sptr = il->section;
}
if (!sptr)
continue;
if (_tcsicmp(sptr, il->section)) {
idx--;
if (idx < 0) {
*section = my_strdup(il->section);
return true;
}
sptr = il->section;
}
}
}
return false;
}
bool ini_getsectionstring(struct ini_data *ini, const TCHAR *section, int idx, TCHAR **keyout, TCHAR **valout)
{
for (int c = 0; c < ini->inilines; c++) {
struct ini_line *il = ini->inidata[c];
if (il && !_tcsicmp(section, il->section)) {
if (idx == 0) {
if (keyout) {
*keyout = my_strdup(il->key);
}
if (valout) {
*valout = my_strdup(il->value);
}
return true;
}
idx--;
}
}
return false;
}
void ini_setcurrentasstart(struct ini_data *ini, struct ini_context *ctx)
{
ctx->start = ctx->lastpos;
}
void ini_setnextasstart(struct ini_data *ini, struct ini_context *ctx)
{
ctx->start = ctx->lastpos + 1;
}
void ini_setlast(struct ini_data *ini, const TCHAR *section, const TCHAR *key, struct ini_context *ctx)
{
for (int c = ctx->start + 1; c < ini->inilines; c++) {
struct ini_line *il = ini->inidata[c];
if (il && !_tcsicmp(section, il->section) && (key == NULL || !_tcsicmp(key, il->key))) {
ctx->end = c;
return;
}
}
}
void ini_setlastasstart(struct ini_data *ini, struct ini_context *ctx)
{
ctx->start = ctx->end;
ctx->end = 0x7fffffff;
}
void ini_initcontext(struct ini_data *ini, struct ini_context *ctx)
{
memset(ctx, 0, sizeof(struct ini_context));
ctx->end = 0x7fffffff;
}
bool ini_addstring(struct ini_data *ini, const TCHAR *section, const TCHAR *key, const TCHAR *val)
{
for (int c = 0; c < ini->inilines; c++) {
struct ini_line *il = ini->inidata[c];
if (il && !_tcsicmp(section, il->section)) {
if (il->key == NULL)
return true;
if (!_tcsicmp(key, il->key)) {
const TCHAR *v = val ? val : _T("");
if (il->value && !_tcscmp(il->value, v))
return true;
xfree(il->value);
il->value = my_strdup(v);
ini->modified = true;
return true;
}
}
}
ini_addnewstring(ini, section, key, val);
ini->modified = true;
return true;
}
bool ini_delete(struct ini_data *ini, const TCHAR *section, const TCHAR *key)
{
bool deleted = false;
for (int c = 0; c < ini->inilines; c++) {
struct ini_line *il = ini->inidata[c];
if (il && !_tcsicmp(section, il->section) && (key == NULL || !_tcsicmp(key, il->key))) {
xfree(il->section);
xfree(il->key);
xfree(il->value);
xfree(il);
ini->inidata[c] = NULL;
ini->modified = true;
deleted = true;
}
}
return deleted;
}