mirror of
https://github.com/LIV2/WinUAE.git
synced 2025-12-06 00:12:52 +00:00
583 lines
13 KiB
C++
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;
|
|
}
|