From 8eddf850f2a7c0288be9b2ad159bcc473d958c55 Mon Sep 17 00:00:00 2001 From: msiewert76 Date: Mon, 25 Apr 2011 20:06:13 +0000 Subject: [PATCH] The source for the 0.2.0 release (12-30-2010). --- arm9/Makefile | 2 +- arm9/source/auxspi.cpp | 14 +- arm9/source/display.cpp | 106 ++- arm9/source/fileselect.cpp | 204 +++-- arm9/source/fileselect.h | 22 +- arm9/source/ftplib.c | 1452 ++++++++++++++++++++++++++++++++++++ arm9/source/ftplib.h | 139 ++++ arm9/source/hardware.cpp | 246 +++++- arm9/source/hardware.h | 3 + arm9/source/headings.i | 394 ++++++++++ arm9/source/ini.cpp | 1165 +++++++++++++++++++++++++++++ arm9/source/ini.h | 76 ++ arm9/source/iniconfig.h | 59 ++ arm9/source/iniheadings.h | 55 ++ arm9/source/inikeys.h | 54 ++ arm9/source/inilist.h | 32 + arm9/source/keys.i | 438 +++++++++++ arm9/source/libini.h | 219 ++++++ arm9/source/list.i | 332 +++++++++ arm9/source/main.cpp | 163 +++- arm9/source/types.i | 530 +++++++++++++ savegame_manager.pnproj | 2 +- savegame_manager.pnps | 2 +- 23 files changed, 5605 insertions(+), 104 deletions(-) create mode 100644 arm9/source/ftplib.c create mode 100644 arm9/source/ftplib.h create mode 100644 arm9/source/headings.i create mode 100644 arm9/source/ini.cpp create mode 100644 arm9/source/ini.h create mode 100644 arm9/source/iniconfig.h create mode 100644 arm9/source/iniheadings.h create mode 100644 arm9/source/inikeys.h create mode 100644 arm9/source/inilist.h create mode 100644 arm9/source/keys.i create mode 100644 arm9/source/libini.h create mode 100644 arm9/source/list.i create mode 100644 arm9/source/types.i diff --git a/arm9/Makefile b/arm9/Makefile index cbf5cd1..9e35932 100644 --- a/arm9/Makefile +++ b/arm9/Makefile @@ -40,7 +40,7 @@ LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project #--------------------------------------------------------------------------------- -LIBS := -lfat -lnds9 +LIBS := -lfat -ldswifi9 -lnds9 #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing diff --git a/arm9/source/auxspi.cpp b/arm9/source/auxspi.cpp index 0b3ddbb..e805643 100644 --- a/arm9/source/auxspi.cpp +++ b/arm9/source/auxspi.cpp @@ -28,6 +28,12 @@ #include "auxspi_core.cpp" #include "hardware.h" +#include + +using std::max; + +extern u8 data[0x8000]; + // ======================================================== // local functions @@ -265,9 +271,13 @@ void auxspi_erase() auxspi_wait_busy(); auxspi_close(); } + } else { + int8 size = 1 << max(0, (auxspi_save_size_log_2() - 15)); + memset(data, 0, 0x8000); + for (int i = 0; i < size; i++) { + auxspi_write_data(i << 15, data, 0x8000, type); + } } - - // FIXME: do something for other types as well! } void auxspi_wait_wip() diff --git a/arm9/source/display.cpp b/arm9/source/display.cpp index 6c378f5..0e84ecb 100644 --- a/arm9/source/display.cpp +++ b/arm9/source/display.cpp @@ -55,7 +55,7 @@ void displayInit() consoleInit(&lowerScreen, 3,BgType_Text4bpp, BgSize_T_256x256, 31, 0, false, true); consoleSelect(&upperScreen); - displayMessage("DS savegame manager\nVersion 0.1.1 Beta\nBy Pokedoc"); + iprintf("\n\n\n\n\nDS savegame manager\nVersion 0.2 Beta\nBy Pokedoc"); displayPrintState("Press (B) to continue"); while (!(keysCurrent() & KEY_B)); @@ -63,6 +63,11 @@ void displayInit() void displayPrintUpper() { + extern uint32 ezflash; + extern bool gba; + extern uint32 dstype; + extern bool slot2; + // print upper screen (background) consoleSelect(&upperScreen); consoleSetWindow(&upperScreen, 0, 0, 32, 24); @@ -73,16 +78,20 @@ void displayPrintUpper() iprintf("Game name:\n"); iprintf("Game save:\n"); iprintf("Special :\n"); -#if 0 iprintf("--- SLOT 2 ---------------------"); - iprintf("Game ID :\n"); - iprintf("Game name:\n"); - iprintf("Game save:\n"); - iprintf("Special :\n"); -#endif + if (dstype > 0) + iprintf("This device has no Slot-2"); + else { + iprintf("Game ID :\n"); + iprintf("Game name:\n"); + iprintf("Game save:\n"); + iprintf("Special :\n"); + } // print upper screen - consoleSetWindow(&upperScreen, 10, 2, 12, 4); + consoleSetWindow(&upperScreen, 10, 2, 22, 4); + consoleClear(); + consoleSetWindow(&upperScreen, 10, 7, 22, 4); consoleClear(); // fetch cartridge header (maybe, calling "cardReadHeader" on a FC messes with libfat!) @@ -108,12 +117,15 @@ void displayPrintUpper() case 3: sprintf(&name[0], "DSi/SD"); break; + case 4: + sprintf(&name[0], "Slot 2"); + break; } consoleClear(); iprintf("%s", name); // 1) The cart id. - consoleSetWindow(&upperScreen, 10, 2, 20, 1); + consoleSetWindow(&upperScreen, 10, 2, 22, 1); sprintf(&name[0], "----"); if (flash_card) { sprintf(&name[0], "Flash Card"); @@ -125,7 +137,7 @@ void displayPrintUpper() iprintf("%s", name); // 2) The cart name. - consoleSetWindow(&upperScreen, 10, 3, 20, 1); + consoleSetWindow(&upperScreen, 10, 3, 22, 1); sprintf(&name[0], "----"); if (flash_card) { sprintf(&name[0], "Flash Card"); @@ -137,7 +149,7 @@ void displayPrintUpper() iprintf("%s", name); // 3) The save type - consoleSetWindow(&upperScreen, 10, 4, 20, 1); + consoleSetWindow(&upperScreen, 10, 4, 22, 1); sprintf(&name[0], "----"); if (flash_card) { sprintf(&name[0], "Flash Card"); @@ -146,10 +158,10 @@ void displayPrintUpper() uint32 size = auxspi_save_size(); switch (type) { case 1: - sprintf(&name[0], "Type 1 (%i Bytes)", size); + sprintf(&name[0], "Eeprom (%i Bytes)", size); break; case 2: - sprintf(&name[0], "Type 2 (%i kB)", size >> 10); + sprintf(&name[0], "FRAM (%i kB)", size >> 10); break; case 3: sprintf(&name[0], "Flash (%i kB)", size >> 10); @@ -163,17 +175,81 @@ void displayPrintUpper() iprintf("%s", name); // 4) Special properties (infrared device...) - consoleSetWindow(&upperScreen, 10, 5, 24, 1); + consoleSetWindow(&upperScreen, 10, 5, 22, 1); consoleClear(); memset(&name[0], 0, MAXPATHLEN); //if (auxspi_has_infrared()) { if (ir) { sprintf(&name[0], "Infrared"); } else { - sprintf(&name[0], "---"); + sprintf(&name[0], "----"); } iprintf("%s", name); + // Slot 2 status + // 5) GBA game id + consoleSetWindow(&upperScreen, 10, 7, 22, 1); + consoleClear(); + memset(&name[0], 0, MAXPATHLEN); + if (ezflash) { + if (ezflash == 0x89168916) + sprintf(name, "3in1 (512M)"); + else + sprintf(name, "3in1 (256M)"); + } else if (gba) + sprintf(name, "%.4s", (char*)0x080000ac); + else if (slot2) + sprintf(name, "Flash Card"); + else if (dstype == 0) + sprintf(name, "----"); + if (dstype == 0) + iprintf("%s", name); + + // 6) GBA game name + consoleSetWindow(&upperScreen, 10, 8, 22, 1); + consoleClear(); + memset(&name[0], 0, MAXPATHLEN); + if (ezflash) + sprintf(name, "3in1"); + else if (gba) + sprintf(name, "%.12s", (char*)0x080000a0); + else if (slot2) + sprintf(name, "Flash Card"); + else if (dstype == 0) + sprintf(name, "----"); + if (dstype == 0) + iprintf(name); + + // 7) GBA save size + consoleSetWindow(&upperScreen, 10, 9, 22, 1); + consoleClear(); + memset(&name[0], 0, MAXPATHLEN); + if (ezflash) + sprintf(name, "SRAM"); + else if (gba) + sprintf(name, "(unsupported)"); + else if (slot2) + sprintf(name, "Flash Card"); + else if (dstype == 0) + sprintf(name, "----"); + if (dstype == 0) + iprintf(name); + + // 8) GBA special stuff + consoleSetWindow(&upperScreen, 10, 10, 22, 1); + consoleClear(); + memset(&name[0], 0, MAXPATHLEN); + if (ezflash) + sprintf(name, "NOR + PSRAM"); + else if (gba) + sprintf(name, "(unsupported)"); + else if (slot2) + sprintf(name, "----"); + else if (dstype == 0) + sprintf(name, "----"); + if (dstype == 0) + iprintf(name); + #if 0 // hack: some tests to debug new hardware. uint8 data[512]; diff --git a/arm9/source/fileselect.cpp b/arm9/source/fileselect.cpp index a2c0a87..1100644 100644 --- a/arm9/source/fileselect.cpp +++ b/arm9/source/fileselect.cpp @@ -33,25 +33,84 @@ #include #include +#include + #include "display.h" -char fnames[256][256]; +#include "fileselect.h" + + +extern u8 data[0x8000]; // --------------------------------------------------------------------------------- +void ftpGetFileList(const char *dir, netbuf *ctrl, uint32 &num) +{ + char *buf = (char*)data; + memset(buf, 0, 0x8000); + FtpDirBuf((char*)data, 0x8000, "/", ctrl); + //FtpDir("/dir.txt", "/", ctrl); + char *ptr = buf; // scan pointer + char *ptr2 = buf; // write pointer + // count number of entries and condense them for further use + char *begin = 0, *end; + while ((end = strchr(ptr, '\n')) != NULL) { + char c; + char fname[512]; + if (!strlen(ptr)) + break; + sscanf(ptr, "%c%*s %*s %*s %*s %*s %*s %*s %*s %[^\n]", &c, fname); // gets 'd' or 'f' - dir or file + begin = strstr(ptr, fname); + if (!begin) + break; + int flen = end - begin + 1; + if (flen <= 1) + break; // some FTP servers send an empty line at the end, so prevent an integer overflow + if (c == 'd') + *ptr2 = 'd'; + else + *ptr2 = 'f'; + ptr2++; + memcpy(ptr2, begin, flen); + ptr = end + 1; + ptr2 += flen; + num++; + } +} + void fileGetFileList(const char *dir, uint32 &num) { + char *buf = (char*)data; + int idx = 0; + memset(buf, 0, 0x8000); + DIR *pdir; struct dirent *pent; num = 0; - pdir=opendir(dir); - + char name[128]; if (pdir) { while ((pent=readdir(pdir)) !=NULL) { - sprintf(&fnames[num][0], "%s", pent->d_name); - num++; - if (num == 256) + char fullname[256]; + sprintf(fullname, "%s/%s", dir, pent->d_name); + struct stat statbuf; + stat(fullname, &statbuf); + + // TODO: check for buffer overflow! + if (idx + strlen(pent->d_name) < 0x8000-2) { + if (S_ISDIR(statbuf.st_mode)) { + int len = sprintf(buf, "d%s\n", pent->d_name); + buf += len; + idx += len; + } else { + int len = sprintf(buf, "f%s\n", pent->d_name); + buf += len; + idx += len; + } + num++; + } else { + // buffer overflow imminent break; + } } } if (pdir) @@ -67,45 +126,65 @@ void filePrintFileList(const char *dir, uint32 first, uint32 select, uint32 coun consoleSetWindow(&lowerScreen, 0, 0, 32, 19); consoleClear(); - for (uint32 i = first; (i < first + 18) && (i < count); i++) { - struct stat statbuf; - char fullpath[512]; - sprintf(fullpath, "%s/%s", dir, fnames[i]); - stat(fullpath, &statbuf); - if (S_ISDIR(statbuf.st_mode)) { - if (i != select) - iprintf("[%.29s]\n", fnames[i]); - else - iprintf("-->[%.26s]\n", fnames[i]); - } else { - if (i != select) - iprintf("%.31s\n", fnames[i]); - else - iprintf("-->%.28s\n", fnames[i]); - } + char *buf = (char*)data; + int idx = 0; + int len = strlen(buf); + for (int i = 0; i < first; i++) { + buf = strchr(buf, '\n') + 1; } + for (uint32 i = first; (i < first + 18) && (i < count); i++) { + struct stat statbuf; + char dir; + u32 size; + char fname[128]; + char fname2[128]; + char *newline = strchr(buf, '\n'); + int linelen = newline - buf; + + *newline = 0; + if (*buf == 'd') { + if (i != select) + iprintf("[%.29s]\n", buf+1); + else + iprintf("-->[%.26s]\n", buf+1); + } else { + if (i != select) + iprintf("%.31s\n", buf+1); + else + iprintf("-->%.28s\n", buf+1); + } + *newline = '\n'; + buf += linelen+1; + } + consoleSelect(&lowerScreen); consoleSetWindow(&lowerScreen, 0, 18, 32, 6); consoleClear(); iprintf("================================"); iprintf("Please select a .sav file\n"); iprintf(" (A) Select\n"); + iprintf(" (B) One directory up\n"); if (cancel) - iprintf(" (B) cancel"); + iprintf(" (L+R) cancel (new file)"); } // ========================***************************============================== -void fileSelect(const char *startdir, char *out_dir, char *out_fname, bool allow_cancel) +void fileSelect(const char *startdir, char *out_dir, char *out_fname, netbuf *buf, + bool allow_cancel, bool allow_up) { bool select = false; + static int size_list = 18; uint32 num_files = 0; uint32 sel_file = 0; uint32 first_file = 0; // get list of files in current dir - fileGetFileList(startdir, num_files); + if (buf) + ftpGetFileList("/", buf, num_files); + else + fileGetFileList(startdir, num_files); filePrintFileList(startdir, first_file, sel_file, num_files, allow_cancel); while (!select) { @@ -116,49 +195,82 @@ void fileSelect(const char *startdir, char *out_dir, char *out_fname, bool allow if (keys & KEY_DOWN) { if (sel_file < num_files-1) { sel_file++; + if (sel_file >= first_file + size_list - 1) + first_file = sel_file - (size_list - 1); filePrintFileList(startdir, first_file, sel_file, num_files, allow_cancel); - if (sel_file > first_file + 18) - first_file = sel_file - 18; } } else if (keys & KEY_UP) { if (sel_file > 0) { sel_file--; - filePrintFileList(startdir, first_file, sel_file, num_files, allow_cancel); if (sel_file < first_file) first_file = sel_file; + filePrintFileList(startdir, first_file, sel_file, num_files, allow_cancel); } } else if (keys & KEY_A) { - // TODO: Okay, my first attempt at implementing a fuill file browser - // failed, so I am disabling changing directory for now. If anybody - // feels inclined to fix it, go ahead. - if (stricmp(fnames[sel_file], ".") == 0) - continue; - if (stricmp(fnames[sel_file], "..") == 0) - //return; + // get selected file name + char *buf2 = (char*)data; + int idx = 0; + int len = strlen(buf2); + char fname[128]; + for (int i = 0; i < sel_file; i++) { + buf2 = strchr(buf2, '\n') + 1; + } + char c; + sscanf(buf2, "%c%[^\n]", &c, fname); // gets 'd' or 'f' - dir or file + // special cases + if (stricmp(fname, ".") == 0) + // don't do anything continue; + if (stricmp(fname, "..") == 0) { + // return to parent function (usually results in "back to higher level") + while (keysCurrent() & (KEY_A | KEY_B)); + if (!allow_up) + continue; + if (buf) + FtpChdir("..", buf); + return; + } + + // get selected file properties char fullname[512]; - sprintf(fullname, "%s/%s", startdir, fnames[sel_file]); + sprintf(fullname, "%s%s", startdir, fname); struct stat statbuf; stat(fullname, &statbuf); - if (S_ISDIR(statbuf.st_mode)) { - /* + if (c == 'd') { char fullpath[512]; - sprintf(fullpath, "%s/%s", startdir, fnames[sel_file]); - fileSelect(fullpath, out_dir, out_fname, allow_cancel); - for (int i = 0; i < 5; i++) - swiWaitForVBlank(); + sprintf(fullpath, "%s%s/", startdir, fname); + if (buf) + FtpChdir(fname, buf); + fileSelect(fullpath, out_dir, out_fname, buf, allow_cancel, true); + if ((keysCurrent() & (KEY_L | KEY_R)) && allow_cancel) + return; + while (keysCurrent() & (KEY_A | KEY_B)); + + // did we select a file? if so, keep returning to parent calling function if (strlen(out_fname)) return; - */ + + // we did not select anything, so regenerate the old list + num_files = 0; + if (buf) + ftpGetFileList("/", buf, num_files); + else + fileGetFileList(startdir, num_files); + filePrintFileList(startdir, first_file, sel_file, num_files, allow_cancel); continue; } else { sprintf(out_dir, "%s", startdir); - sprintf(out_fname, "%s", fnames[sel_file]); + sprintf(out_fname, "%s", fname); + while (keysCurrent() & (KEY_A | KEY_B)); return; } - select = true; - } else if (keys & KEY_B) { + if (!allow_up) + continue; + if (buf) + FtpChdir("..", buf); + return; + } else if (keys & (KEY_L | KEY_R)) { if (allow_cancel) { sprintf(out_dir, "%s", startdir); out_fname[0] = 0; diff --git a/arm9/source/fileselect.h b/arm9/source/fileselect.h index 1d239aa..650a941 100644 --- a/arm9/source/fileselect.h +++ b/arm9/source/fileselect.h @@ -33,8 +33,28 @@ #define MAX_CUSTOM_LIST 8192 +#include +//#endif +//#if defined(_WIN32) +//#include +//#endif +#include +#include +#include +#include +#include +//#if defined(__unix__) +#include +#include +#include +#include +#include -void fileSelect(const char *startdir, char *out_dir, char *out_fname, bool allow_cancel = false); +#include "ftplib.h" + + +void fileSelect(const char *startdir, char *out_dir, char *out_fname, netbuf *buf = 0, + bool allow_cancel = false, bool allow_up = true); bool fileExists(const char *fname); uint32 fileSize(const char *fname); diff --git a/arm9/source/ftplib.c b/arm9/source/ftplib.c new file mode 100644 index 0000000..d7a9609 --- /dev/null +++ b/arm9/source/ftplib.c @@ -0,0 +1,1452 @@ +/***************************************************************************/ +/* */ +/* ftplib.c - callable ftp access routines */ +/* Copyright (C) 1996-2001 Thomas Pfau, pfau@eclipse.net */ +/* 1407 Thomas Ave, North Brunswick, NJ, 08902 */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library 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 */ +/* Library General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Library General Public */ +/* License along with this progam; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/***************************************************************************/ + +//#if defined(__unix__) || defined(__VMS) +#include +//#endif +//#if defined(_WIN32) +//#include +//#endif +#include +#include +#include +#include +#include +//#if defined(__unix__) +#include +#include +#include +#include +#include +//#include +//#include +/* +#elif defined(VMS) +#include +#include +#include +#include +#include +#elif defined(_WIN32) +#include +#endif +*/ + +#define IPPROTO_TCP 0 + +//#define BUILDING_LIBRARY +#include "ftplib.h" + +//#if defined(_WIN32) +#define SETSOCKOPT_OPTVAL_TYPE (const char *) +//#else +//#define SETSOCKOPT_OPTVAL_TYPE (void *) +//#endif + +#define FTPLIB_BUFSIZ 8192 +#define ACCEPT_TIMEOUT 30 + +#define FTPLIB_CONTROL 0 +#define FTPLIB_READ 1 +#define FTPLIB_WRITE 2 + +#if !defined FTPLIB_DEFMODE +#define FTPLIB_DEFMODE FTPLIB_PASSIVE +#endif + +#include "display.h" +#define perror iprintf + +struct NetBuf { + char *cput,*cget; + int handle; + int cavail,cleft; + char *buf; + int dir; + netbuf *ctrl; + netbuf *data; + int cmode; + struct timeval idletime; + FtpCallback idlecb; + void *idlearg; + int xfered; + int cbbytes; + int xfered1; + char response[256]; +}; + +struct linger { + int l_onoff; + int l_linger; +}; + +/* +static char *version = + "ftplib Release 3.1-1 9/16/00, copyright 1996-2000 Thomas Pfau"; +*/ + +GLOBALDEF int ftplib_debug = 0; + +//#if defined(__unix__) || defined(VMS) +//#define net_read read +//#define net_write write +//#define net_close close +//#elif defined(_WIN32) +#define net_read(x,y,z) recv(x,y,z,0) +#define net_write(x,y,z) send(x,y,z,0) +#define net_close closesocket +//#endif + +#if defined(NEED_MEMCCPY) +/* + * VAX C does not supply a memccpy routine so I provide my own + */ +void *memccpy(void *dest, const void *src, int c, size_t n) +{ + int i=0; + const unsigned char *ip=src; + unsigned char *op=dest; + + while (i < n) + { + if ((*op++ = *ip++) == c) + break; + i++; + } + if (i == n) + return NULL; + return op; +} +#endif +#if defined(NEED_STRDUP) +/* + * strdup - return a malloc'ed copy of a string + */ +char *strdup(const char *src) +{ + int l = strlen(src) + 1; + char *dst = malloc(l); + if (dst) + strcpy(dst,src); + return dst; +} +#endif + +/* + * socket_wait - wait for socket to receive or flush data + * + * return 1 if no user callback, otherwise, return value returned by + * user callback + */ +static int socket_wait(netbuf *ctl) +{ + fd_set fd,*rfd = NULL,*wfd = NULL; + struct timeval tv; + int rv = 0; + if ((ctl->dir == FTPLIB_CONTROL) || (ctl->idlecb == NULL)) + return 1; + if (ctl->dir == FTPLIB_WRITE) + wfd = &fd; + else + rfd = &fd; + FD_ZERO(&fd); + do + { + FD_SET(ctl->handle,&fd); + tv = ctl->idletime; + rv = select(ctl->handle+1, rfd, wfd, NULL, &tv); + if (rv == -1) + { + rv = 0; + strncpy(ctl->ctrl->response, strerror(errno), + sizeof(ctl->ctrl->response)); + break; + } + else if (rv > 0) + { + rv = 1; + break; + } + } + while ((rv = ctl->idlecb(ctl, ctl->xfered, ctl->idlearg))); + return rv; +} + +/* + * read a line of text + * + * return -1 on error or bytecount + */ +static int readline(char *buf,int max,netbuf *ctl) +{ + int x,retval = 0; + char *end,*bp=buf; + int eof = 0; + + if ((ctl->dir != FTPLIB_CONTROL) && (ctl->dir != FTPLIB_READ)) + return -1; + if (max == 0) + return 0; + do + { + if (ctl->cavail > 0) + { + x = (max >= ctl->cavail) ? ctl->cavail : max-1; + end = memccpy(bp,ctl->cget,'\n',x); + if (end != NULL) + x = end - bp; + retval += x; + bp += x; + *bp = '\0'; + max -= x; + ctl->cget += x; + ctl->cavail -= x; + if (end != NULL) + { + bp -= 2; + if (strcmp(bp,"\r\n") == 0) + { + *bp++ = '\n'; + *bp++ = '\0'; + --retval; + } + break; + } + } + if (max == 1) + { + *buf = '\0'; + break; + } + if (ctl->cput == ctl->cget) + { + ctl->cput = ctl->cget = ctl->buf; + ctl->cavail = 0; + ctl->cleft = FTPLIB_BUFSIZ; + } + if (eof) + { + if (retval == 0) + retval = -1; + break; + } + if (!socket_wait(ctl)) + return retval; + if ((x = net_read(ctl->handle,ctl->cput,ctl->cleft)) == -1) + { + perror("read"); + retval = -1; + break; + } + if (x == 0) + eof = 1; + ctl->cleft -= x; + ctl->cavail += x; + ctl->cput += x; + } + while (1); + return retval; +} + +/* + * write lines of text + * + * return -1 on error or bytecount + */ +static int writeline(char *buf, int len, netbuf *nData) +{ + int x, nb=0, w; + char *ubp = buf, *nbp; + char lc=0; + + if (nData->dir != FTPLIB_WRITE) + return -1; + nbp = nData->buf; + for (x=0; x < len; x++) + { + if ((*ubp == '\n') && (lc != '\r')) + { + if (nb == FTPLIB_BUFSIZ) + { + if (!socket_wait(nData)) + return x; + w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ); + if (w != FTPLIB_BUFSIZ) + { + printf("net_write(1) returned %d, errno = %d\n", w, errno); + return(-1); + } + nb = 0; + } + nbp[nb++] = '\r'; + } + if (nb == FTPLIB_BUFSIZ) + { + if (!socket_wait(nData)) + return x; + w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ); + if (w != FTPLIB_BUFSIZ) + { + printf("net_write(2) returned %d, errno = %d\n", w, errno); + return(-1); + } + nb = 0; + } + nbp[nb++] = lc = *ubp++; + } + if (nb) + { + if (!socket_wait(nData)) + return x; + w = net_write(nData->handle, nbp, nb); + if (w != nb) + { + printf("net_write(3) returned %d, errno = %d\n", w, errno); + return(-1); + } + } + return len; +} + +/* + * read a response from the server + * + * return 0 if first char doesn't match + * return 1 if first char matches + */ +static int readresp(char c, netbuf *nControl) +{ + char match[5]; + if (readline(nControl->response,256,nControl) == -1) + { + perror("Control socket read failed"); + return 0; + } + if (ftplib_debug > 1) + fprintf(stderr,"%s",nControl->response); + if (nControl->response[3] == '-') + { + strncpy(match,nControl->response,3); + match[3] = ' '; + match[4] = '\0'; + do + { + if (readline(nControl->response,256,nControl) == -1) + { + perror("Control socket read failed"); + return 0; + } + if (ftplib_debug > 1) + fprintf(stderr,"%s",nControl->response); + } + while (strncmp(nControl->response,match,4)); + } + if (nControl->response[0] == c) + return 1; + return 0; +} + +/* + * FtpInit for stupid operating systems that require it (Windows NT) + */ +GLOBALDEF void FtpInit(void) +{ +/* +#if defined(_WIN32) + WORD wVersionRequested; + WSADATA wsadata; + int err; + wVersionRequested = MAKEWORD(1,1); + if ((err = WSAStartup(wVersionRequested,&wsadata)) != 0) + fprintf(stderr,"Network failed to start: %d\n",err); +#endif +*/ +} + +/* + * FtpLastResponse - return a pointer to the last response received + */ +GLOBALDEF char *FtpLastResponse(netbuf *nControl) +{ + if ((nControl) && (nControl->dir == FTPLIB_CONTROL)) + return nControl->response; + return NULL; +} + +/* + * FtpConnect - connect to remote server + * + * return 1 if connected, 0 if not + */ +GLOBALDEF int FtpConnect(const char *host, netbuf **nControl) +{ + int sControl; + struct sockaddr_in sin; + //struct hostent *phe; + //struct servent *pse; + int on=1; + netbuf *ctrl; + char *lhost; + char *pnum; + + memset(&sin,0,sizeof(sin)); + sin.sin_family = AF_INET; + lhost = strdup(host); + pnum = strchr(lhost,':'); + *pnum++ = '\0'; + if (isdigit(*pnum)) + sin.sin_port = htons((short)atoi(pnum)); + /* + else + { + pse = getservbyname(pnum,"tcp"); + sin.sin_port = pse->s_port; + } + */ + u32 v[5]; + sscanf(host,"%u.%u.%u.%u:%u",&v[1],&v[2],&v[3],&v[4],&v[0]); + sin.sin_port = htons(v[0]); + sin.sin_addr.s_addr = (v[1] << 0) + (v[2] << 8) + (v[3] << 16) + (v[4] << 24); + /* + if ((sin.sin_addr.s_addr = inet_addr(lhost)) == -1) + { + if ((phe = gethostbyname(lhost)) == NULL) + { + perror("gethostbyname"); + return 0; + } + memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length); + } + */ + free(lhost); + sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sControl == -1) + { + perror("socket"); + return 0; + } + if (setsockopt(sControl,SOL_SOCKET,SO_REUSEADDR, + SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1) + { + perror("setsockopt"); + net_close(sControl); + return 0; + } + // FIXME: connect breaks! + if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1) + { + perror("connect"); + net_close(sControl); + return 0; + } + ctrl = calloc(1,sizeof(netbuf)); + if (ctrl == NULL) + { + perror("calloc"); + net_close(sControl); + return 0; + } + ctrl->buf = malloc(FTPLIB_BUFSIZ); + if (ctrl->buf == NULL) + { + perror("calloc"); + net_close(sControl); + free(ctrl); + return 0; + } + ctrl->handle = sControl; + ctrl->dir = FTPLIB_CONTROL; + ctrl->ctrl = NULL; + ctrl->cmode = FTPLIB_DEFMODE; + ctrl->idlecb = NULL; + ctrl->idletime.tv_sec = ctrl->idletime.tv_usec = 0; + ctrl->idlearg = NULL; + ctrl->xfered = 0; + ctrl->xfered1 = 0; + ctrl->cbbytes = 0; + if (readresp('2', ctrl) == 0) + { + net_close(sControl); + free(ctrl->buf); + free(ctrl); + return 0; + } + *nControl = ctrl; + return 1; +} + +/* + * FtpOptions - change connection options + * + * returns 1 if successful, 0 on error + */ +GLOBALDEF int FtpOptions(int opt, long val, netbuf *nControl) +{ + int v,rv=0; + switch (opt) + { + case FTPLIB_CONNMODE: + v = (int) val; + if ((v == FTPLIB_PASSIVE) || (v == FTPLIB_PORT)) + { + nControl->cmode = v; + rv = 1; + } + break; + case FTPLIB_CALLBACK: + nControl->idlecb = (FtpCallback) val; + rv = 1; + break; + case FTPLIB_IDLETIME: + v = (int) val; + rv = 1; + nControl->idletime.tv_sec = v / 1000; + nControl->idletime.tv_usec = (v % 1000) * 1000; + break; + case FTPLIB_CALLBACKARG: + rv = 1; + nControl->idlearg = (void *) val; + break; + case FTPLIB_CALLBACKBYTES: + rv = 1; + nControl->cbbytes = (int) val; + break; + } + return rv; +} + +/* + * FtpSendCmd - send a command and wait for expected response + * + * return 1 if proper response received, 0 otherwise + */ +static int FtpSendCmd(const char *cmd, char expresp, netbuf *nControl) +{ + char buf[256]; + if (nControl->dir != FTPLIB_CONTROL) + return 0; + if (ftplib_debug > 2) + fprintf(stderr,"%s\n",cmd); + if ((strlen(cmd) + 3) > sizeof(buf)) + return 0; + sprintf(buf,"%s\r\n",cmd); + if (net_write(nControl->handle,buf,strlen(buf)) <= 0) + { + perror("write"); + return 0; + } + return readresp(expresp, nControl); +} + +/* + * FtpLogin - log in to remote server + * + * return 1 if logged in, 0 otherwise + */ +GLOBALDEF int FtpLogin(const char *user, const char *pass, netbuf *nControl) +{ + char tempbuf[64]; + + if (((strlen(user) + 7) > sizeof(tempbuf)) || + ((strlen(pass) + 7) > sizeof(tempbuf))) + return 0; + sprintf(tempbuf,"USER %s",user); + if (!FtpSendCmd(tempbuf,'3',nControl)) + { + if (nControl->response[0] == '2') + return 1; + return 0; + } + sprintf(tempbuf,"PASS %s",pass); + return FtpSendCmd(tempbuf,'2',nControl); +} + +/* + * FtpOpenPort - set up data connection + * + * return 1 if successful, 0 otherwise + */ +static int FtpOpenPort(netbuf *nControl, netbuf **nData, int mode, int dir) +{ + int sData; + union { + struct sockaddr sa; + struct sockaddr_in in; + } sin; + struct linger lng = { 0, 0 }; + unsigned int l; + int on=1; + netbuf *ctrl; + char *cp; + unsigned int v[6]; + char buf[256]; + + if (nControl->dir != FTPLIB_CONTROL) + return -1; + if ((dir != FTPLIB_READ) && (dir != FTPLIB_WRITE)) + { + sprintf(nControl->response, "Invalid direction %d\n", dir); + return -1; + } + if ((mode != FTPLIB_ASCII) && (mode != FTPLIB_IMAGE)) + { + sprintf(nControl->response, "Invalid mode %c\n", mode); + return -1; + } + l = sizeof(sin); + if (nControl->cmode == FTPLIB_PASSIVE) + { + memset(&sin, 0, l); + sin.in.sin_family = AF_INET; + if (!FtpSendCmd("PASV",'2',nControl)) + return -1; + cp = strchr(nControl->response,'('); + if (cp == NULL) + return -1; + cp++; + sscanf(cp,"%u,%u,%u,%u,%u,%u",&v[2],&v[3],&v[4],&v[5],&v[0],&v[1]); + sin.sa.sa_data[2] = v[2]; + sin.sa.sa_data[3] = v[3]; + sin.sa.sa_data[4] = v[4]; + sin.sa.sa_data[5] = v[5]; + sin.sa.sa_data[0] = v[0]; + sin.sa.sa_data[1] = v[1]; + } + else + { + if (getsockname(nControl->handle, &sin.sa, (int*)&l) < 0) + { + perror("getsockname"); + return 0; + } + } + sData = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (sData == -1) + { + perror("socket"); + return -1; + } + if (setsockopt(sData,SOL_SOCKET,SO_REUSEADDR, + SETSOCKOPT_OPTVAL_TYPE &on,sizeof(on)) == -1) + { + perror("setsockopt"); + net_close(sData); + return -1; + } + if (setsockopt(sData,SOL_SOCKET,SO_LINGER, + SETSOCKOPT_OPTVAL_TYPE &lng,sizeof(lng)) == -1) + { + perror("setsockopt"); + net_close(sData); + return -1; + } + if (nControl->cmode == FTPLIB_PASSIVE) + { + if (connect(sData, &sin.sa, sizeof(sin.sa)) == -1) + { + perror("connect"); + net_close(sData); + return -1; + } + } + else + { + sin.in.sin_port = 0; + if (bind(sData, &sin.sa, sizeof(sin)) == -1) + { + perror("bind"); + net_close(sData); + return 0; + } + if (listen(sData, 1) < 0) + { + perror("listen"); + net_close(sData); + return 0; + } + if (getsockname(sData, &sin.sa, (int*)&l) < 0) + return 0; + sprintf(buf, "PORT %d,%d,%d,%d,%d,%d", + (unsigned char) sin.sa.sa_data[2], + (unsigned char) sin.sa.sa_data[3], + (unsigned char) sin.sa.sa_data[4], + (unsigned char) sin.sa.sa_data[5], + (unsigned char) sin.sa.sa_data[0], + (unsigned char) sin.sa.sa_data[1]); + if (!FtpSendCmd(buf,'2',nControl)) + { + net_close(sData); + return 0; + } + } + ctrl = calloc(1,sizeof(netbuf)); + if (ctrl == NULL) + { + perror("calloc"); + net_close(sData); + return -1; + } + if ((mode == 'A') && ((ctrl->buf = malloc(FTPLIB_BUFSIZ)) == NULL)) + { + perror("calloc"); + net_close(sData); + free(ctrl); + return -1; + } + ctrl->handle = sData; + ctrl->dir = dir; + ctrl->idletime = nControl->idletime; + ctrl->idlearg = nControl->idlearg; + ctrl->xfered = 0; + ctrl->xfered1 = 0; + ctrl->cbbytes = nControl->cbbytes; + if (ctrl->idletime.tv_sec || ctrl->idletime.tv_usec || ctrl->cbbytes) + ctrl->idlecb = nControl->idlecb; + else + ctrl->idlecb = NULL; + *nData = ctrl; + return 1; +} + +/* + * FtpAcceptConnection - accept connection from server + * + * return 1 if successful, 0 otherwise + */ +static int FtpAcceptConnection(netbuf *nData, netbuf *nControl) +{ + int sData; + struct sockaddr addr; + unsigned int l; + int i; + struct timeval tv; + fd_set mask; + int rv; + + FD_ZERO(&mask); + FD_SET(nControl->handle, &mask); + FD_SET(nData->handle, &mask); + tv.tv_usec = 0; + tv.tv_sec = ACCEPT_TIMEOUT; + i = nControl->handle; + if (i < nData->handle) + i = nData->handle; + i = select(i+1, &mask, NULL, NULL, &tv); + if (i == -1) + { + strncpy(nControl->response, strerror(errno), + sizeof(nControl->response)); + net_close(nData->handle); + nData->handle = 0; + rv = 0; + } + else if (i == 0) + { + strcpy(nControl->response, "timed out waiting for connection"); + net_close(nData->handle); + nData->handle = 0; + rv = 0; + } + else + { + if (FD_ISSET(nData->handle, &mask)) + { + l = sizeof(addr); + sData = accept(nData->handle, &addr, (int*)&l); + i = errno; + net_close(nData->handle); + if (sData > 0) + { + rv = 1; + nData->handle = sData; + } + else + { + strncpy(nControl->response, strerror(i), + sizeof(nControl->response)); + nData->handle = 0; + rv = 0; + } + } + else if (FD_ISSET(nControl->handle, &mask)) + { + net_close(nData->handle); + nData->handle = 0; + readresp('2', nControl); + rv = 0; + } + } + return rv; +} + +/* + * FtpAccess - return a handle for a data stream + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpAccess(const char *path, int typ, int mode, netbuf *nControl, + netbuf **nData) +{ + char buf[256]; + int dir; + if ((path == NULL) && + ((typ == FTPLIB_FILE_WRITE) || (typ == FTPLIB_FILE_READ))) + { + sprintf(nControl->response, + "Missing path argument for file transfer\n"); + return 0; + } + sprintf(buf, "TYPE %c", mode); + if (!FtpSendCmd(buf, '2', nControl)) + return 0; + switch (typ) + { + case FTPLIB_DIR: + strcpy(buf,"NLST"); + dir = FTPLIB_READ; + break; + case FTPLIB_DIR_VERBOSE: + strcpy(buf,"LIST"); + dir = FTPLIB_READ; + break; + case FTPLIB_FILE_READ: + strcpy(buf,"RETR"); + dir = FTPLIB_READ; + break; + case FTPLIB_FILE_WRITE: + strcpy(buf,"STOR"); + dir = FTPLIB_WRITE; + break; + default: + sprintf(nControl->response, "Invalid open type %d\n", typ); + return 0; + } + if (path != NULL) + { + int i = strlen(buf); + buf[i++] = ' '; + if ((strlen(path) + i) >= sizeof(buf)) + return 0; + strcpy(&buf[i],path); + } + if (FtpOpenPort(nControl, nData, mode, dir) == -1) + return 0; + if (!FtpSendCmd(buf, '1', nControl)) + { + FtpClose(*nData); + *nData = NULL; + return 0; + } + (*nData)->ctrl = nControl; + nControl->data = *nData; + if (nControl->cmode == FTPLIB_PORT) + { + if (!FtpAcceptConnection(*nData,nControl)) + { + FtpClose(*nData); + *nData = NULL; + nControl->data = NULL; + return 0; + } + } + return 1; +} + +/* + * FtpRead - read from a data connection + */ +GLOBALDEF int FtpRead(void *buf, int max, netbuf *nData) +{ + int i; + if (nData->dir != FTPLIB_READ) + return 0; + if (nData->buf) + i = readline(buf, max, nData); + else + { + i = socket_wait(nData); + if (i != 1) + return 0; + i = net_read(nData->handle, buf, max); + } + if (i == -1) + return 0; + nData->xfered += i; + if (nData->idlecb && nData->cbbytes) + { + nData->xfered1 += i; + if (nData->xfered1 > nData->cbbytes) + { + if (nData->idlecb(nData, nData->xfered, nData->idlearg) == 0) + return 0; + nData->xfered1 = 0; + } + } + return i; +} + +/* + * FtpWrite - write to a data connection + */ +GLOBALDEF int FtpWrite(void *buf, int len, netbuf *nData) +{ + int i; + if (nData->dir != FTPLIB_WRITE) + return 0; + if (nData->buf) + i = writeline(buf, len, nData); + else + { + socket_wait(nData); + i = net_write(nData->handle, buf, len); + } + if (i == -1) + return 0; + nData->xfered += i; + if (nData->idlecb && nData->cbbytes) + { + nData->xfered1 += i; + if (nData->xfered1 > nData->cbbytes) + { + nData->idlecb(nData, nData->xfered, nData->idlearg); + nData->xfered1 = 0; + } + } + return i; +} + +/* + * FtpClose - close a data connection + */ +GLOBALDEF int FtpClose(netbuf *nData) +{ + netbuf *ctrl; + switch (nData->dir) + { + case FTPLIB_WRITE: + /* potential problem - if buffer flush fails, how to notify user? */ + if (nData->buf != NULL) + writeline(NULL, 0, nData); + case FTPLIB_READ: + if (nData->buf) + free(nData->buf); + shutdown(nData->handle,2); + net_close(nData->handle); + ctrl = nData->ctrl; + free(nData); + if (ctrl) + { + ctrl->data = NULL; + return(readresp('2', ctrl)); + } + return 1; + case FTPLIB_CONTROL: + if (nData->data) + { + nData->ctrl = NULL; + FtpClose(nData); + } + net_close(nData->handle); + free(nData); + return 0; + } + return 1; +} + +/* + * FtpSite - send a SITE command + * + * return 1 if command successful, 0 otherwise + */ +GLOBALDEF int FtpSite(const char *cmd, netbuf *nControl) +{ + char buf[256]; + + if ((strlen(cmd) + 7) > sizeof(buf)) + return 0; + sprintf(buf,"SITE %s",cmd); + if (!FtpSendCmd(buf,'2',nControl)) + return 0; + return 1; +} + +/* + * FtpSysType - send a SYST command + * + * Fills in the user buffer with the remote system type. If more + * information from the response is required, the user can parse + * it out of the response buffer returned by FtpLastResponse(). + * + * return 1 if command successful, 0 otherwise + */ +GLOBALDEF int FtpSysType(char *buf, int max, netbuf *nControl) +{ + int l = max; + char *b = buf; + char *s; + if (!FtpSendCmd("SYST",'2',nControl)) + return 0; + s = &nControl->response[4]; + while ((--l) && (*s != ' ')) + *b++ = *s++; + *b++ = '\0'; + return 1; +} + +/* + * FtpMkdir - create a directory at server + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpMkdir(const char *path, netbuf *nControl) +{ + char buf[256]; + + if ((strlen(path) + 6) > sizeof(buf)) + return 0; + sprintf(buf,"MKD %s",path); + if (!FtpSendCmd(buf,'2', nControl)) + return 0; + return 1; +} + +/* + * FtpChdir - change path at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpChdir(const char *path, netbuf *nControl) +{ + char buf[256]; + + if ((strlen(path) + 6) > sizeof(buf)) + return 0; + sprintf(buf,"CWD %s",path); + if (!FtpSendCmd(buf,'2',nControl)) + return 0; + return 1; +} + +/* + * FtpCDUp - move to parent directory at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpCDUp(netbuf *nControl) +{ + if (!FtpSendCmd("CDUP",'2',nControl)) + return 0; + return 1; +} + +/* + * FtpRmdir - remove directory at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpRmdir(const char *path, netbuf *nControl) +{ + char buf[256]; + + if ((strlen(path) + 6) > sizeof(buf)) + return 0; + sprintf(buf,"RMD %s",path); + if (!FtpSendCmd(buf,'2',nControl)) + return 0; + return 1; +} + +/* + * FtpPwd - get working directory at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpPwd(char *path, int max, netbuf *nControl) +{ + int l = max; + char *b = path; + char *s; + if (!FtpSendCmd("PWD",'2',nControl)) + return 0; + s = strchr(nControl->response, '"'); + if (s == NULL) + return 0; + s++; + while ((--l) && (*s) && (*s != '"')) + *b++ = *s++; + *b++ = '\0'; + return 1; +} + +/* + * FtpXfer - issue a command and transfer data + * + * return 1 if successful, 0 otherwise + */ +static int FtpXfer(const char *localfile, const char *path, + netbuf *nControl, int typ, int mode) +{ + int l,c; + char *dbuf; + FILE *local = NULL; + netbuf *nData; + int rv=1; + + if (localfile != NULL) + { + char ac[4] = "w"; + if (typ == FTPLIB_FILE_WRITE) + ac[0] = 'r'; + if (mode == FTPLIB_IMAGE) + ac[1] = 'b'; + local = fopen(localfile, ac); + if (local == NULL) + { + strncpy(nControl->response, strerror(errno), + sizeof(nControl->response)); + return 0; + } + } + if (local == NULL) + local = (typ == FTPLIB_FILE_WRITE) ? stdin : stdout; + if (!FtpAccess(path, typ, mode, nControl, &nData)) + return 0; + dbuf = malloc(FTPLIB_BUFSIZ); + if (typ == FTPLIB_FILE_WRITE) + { + while ((l = fread(dbuf, 1, FTPLIB_BUFSIZ, local)) > 0) + if ((c = FtpWrite(dbuf, l, nData)) < l) + { + printf("short write: passed %d, wrote %d\n", l, c); + rv = 0; + break; + } + } + else + { + while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nData)) > 0) + if (fwrite(dbuf, 1, l, local) <= 0) + { + perror("localfile write"); + rv = 0; + break; + } + } + free(dbuf); + fflush(local); + if (localfile != NULL) + fclose(local); + FtpClose(nData); + return rv; +} + +/* + * FtpNlst - issue an NLST command and write response to output + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpNlst(const char *outputfile, const char *path, + netbuf *nControl) +{ + return FtpXfer(outputfile, path, nControl, FTPLIB_DIR, FTPLIB_ASCII); +} + +/* + * FtpDir - issue a LIST command and write response to output + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpDir(const char *outputfile, const char *path, netbuf *nControl) +{ + return FtpXfer(outputfile, path, nControl, FTPLIB_DIR_VERBOSE, FTPLIB_ASCII); +} + +/* + * FtpSize - determine the size of a remote file + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpSize(const char *path, int *size, char mode, netbuf *nControl) +{ + char cmd[256]; + int resp,sz,rv=1; + + if ((strlen(path) + 7) > sizeof(cmd)) + return 0; + sprintf(cmd, "TYPE %c", mode); + if (!FtpSendCmd(cmd, '2', nControl)) + return 0; + sprintf(cmd,"SIZE %s",path); + if (!FtpSendCmd(cmd,'2',nControl)) + rv = 0; + else + { + if (sscanf(nControl->response, "%d %d", &resp, &sz) == 2) + *size = sz; + else + rv = 0; + } + return rv; +} + +/* + * FtpModDate - determine the modification date of a remote file + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpModDate(const char *path, char *dt, int max, netbuf *nControl) +{ + char buf[256]; + int rv = 1; + + if ((strlen(path) + 7) > sizeof(buf)) + return 0; + sprintf(buf,"MDTM %s",path); + if (!FtpSendCmd(buf,'2',nControl)) + rv = 0; + else + strncpy(dt, &nControl->response[4], max); + return rv; +} + +/* + * FtpGet - issue a GET command and write received data to output + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpGet(const char *outputfile, const char *path, + char mode, netbuf *nControl) +{ + return FtpXfer(outputfile, path, nControl, FTPLIB_FILE_READ, mode); +} + +/* + * FtpPut - issue a PUT command and send data from input + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpPut(const char *inputfile, const char *path, char mode, + netbuf *nControl) +{ + return FtpXfer(inputfile, path, nControl, FTPLIB_FILE_WRITE, mode); +} + +/* + * FtpRename - rename a file at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpRename(const char *src, const char *dst, netbuf *nControl) +{ + char cmd[256]; + + if (((strlen(src) + 7) > sizeof(cmd)) || + ((strlen(dst) + 7) > sizeof(cmd))) + return 0; + sprintf(cmd,"RNFR %s",src); + if (!FtpSendCmd(cmd,'3',nControl)) + return 0; + sprintf(cmd,"RNTO %s",dst); + if (!FtpSendCmd(cmd,'2',nControl)) + return 0; + return 1; +} + +/* + * FtpDelete - delete a file at remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF int FtpDelete(const char *fnm, netbuf *nControl) +{ + char cmd[256]; + + if ((strlen(fnm) + 7) > sizeof(cmd)) + return 0; + sprintf(cmd,"DELE %s",fnm); + if (!FtpSendCmd(cmd,'2', nControl)) + return 0; + return 1; +} + +/* + * FtpQuit - disconnect from remote + * + * return 1 if successful, 0 otherwise + */ +GLOBALDEF void FtpQuit(netbuf *nControl) +{ + if (nControl->dir != FTPLIB_CONTROL) + return; + FtpSendCmd("QUIT",'2',nControl); + net_close(nControl->handle); + free(nControl->buf); + free(nControl); +} + +// -------------------------- +// New functions for the DS. They do essentially the same as the old functions, but will +// not dump data into a file, but into a buffer instead. +// +/* + * FtpDir - issue a LIST command and write response to output + * + * return 1 if successful, 0 otherwise + */ +static int FtpXferBuf(char *buffer, int size, const char *path, + netbuf *nControl, int typ, int mode) +{ + int l,c; + //char *dbuf; + //FILE *local = NULL; + netbuf *nData; + int rv=1; + +/* + if (buffer != NULL) + { + char ac[4] = "w"; + if (typ == FTPLIB_FILE_WRITE) + ac[0] = 'r'; + if (mode == FTPLIB_IMAGE) + ac[1] = 'b'; + local = fopen(localfile, ac); + if (local == NULL) + { + strncpy(nControl->response, strerror(errno), + sizeof(nControl->response)); + return 0; + } + } + if (local == NULL) + local = (typ == FTPLIB_FILE_WRITE) ? stdin : stdout; + */ + if (!FtpAccess(path, typ, mode, nControl, &nData)) { + perror("File access failed"); + return 0; + } + if (typ == FTPLIB_FILE_WRITE) + { + //while ((l = fread(buffer, 1, size, local)) > 0) + if ((c = FtpWrite(buffer, size, nData)) < l) + { + printf("short write: passed %d, wrote %d\n", l, c); + rv = 0; + //break; + } + } + else + { + while ((l = FtpRead(buffer, size, nData)) > 0) { + buffer += l; + size -= l; + } + /* + { + perror("localfile write"); + rv = 0; + //break; + } + */ + } + //free(dbuf); + //fflush(local); + //if (localfile != NULL) + //fclose(local); + FtpClose(nData); + return rv; +} + +GLOBALDEF int FtpDirBuf(char *buffer, int size, const char *path, netbuf *nControl) +{ + return FtpXferBuf(buffer, size, path, nControl, FTPLIB_DIR_VERBOSE, FTPLIB_ASCII); +} + +GLOBALDEF int FtpAccessBuf(const char *path, int typ, int mode, netbuf *nControl, + netbuf **nData) +{ + char buf[256]; + int dir; + if ((path == NULL) && + ((typ == FTPLIB_FILE_WRITE) || (typ == FTPLIB_FILE_READ))) + { + sprintf(nControl->response, + "Missing path argument for file transfer\n"); + return 0; + } + sprintf(buf, "TYPE %c", mode); + if (!FtpSendCmd(buf, '2', nControl)) + return 0; + switch (typ) + { + case FTPLIB_DIR: + strcpy(buf,"NLST"); + dir = FTPLIB_READ; + break; + case FTPLIB_DIR_VERBOSE: + strcpy(buf,"LIST"); + dir = FTPLIB_READ; + break; + case FTPLIB_FILE_READ: + strcpy(buf,"RETR"); + dir = FTPLIB_READ; + break; + case FTPLIB_FILE_WRITE: + strcpy(buf,"STOR"); + dir = FTPLIB_WRITE; + break; + default: + sprintf(nControl->response, "Invalid open type %d\n", typ); + return 0; + } + if (path != NULL) + { + int i = strlen(buf); + buf[i++] = ' '; + if ((strlen(path) + i) >= sizeof(buf)) + return 0; + strcpy(&buf[i],path); + } + if (FtpOpenPort(nControl, nData, mode, dir) == -1) + return 0; + if (!FtpSendCmd(buf, '1', nControl)) + { + FtpClose(*nData); + *nData = NULL; + return 0; + } + (*nData)->ctrl = nControl; + nControl->data = *nData; + if (nControl->cmode == FTPLIB_PORT) + { + if (!FtpAcceptConnection(*nData,nControl)) + { + FtpClose(*nData); + *nData = NULL; + nControl->data = NULL; + return 0; + } + } + return 1; +} + diff --git a/arm9/source/ftplib.h b/arm9/source/ftplib.h new file mode 100644 index 0000000..86b990b --- /dev/null +++ b/arm9/source/ftplib.h @@ -0,0 +1,139 @@ +/***************************************************************************/ +/* */ +/* ftplib.h - header file for callable ftp access routines */ +/* Copyright (C) 1996, 1997 Thomas Pfau, pfau@cnj.digex.net */ +/* 73 Catherine Street, South Bound Brook, NJ, 08880 */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library 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 */ +/* Library General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Library General Public */ +/* License along with this progam; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/***************************************************************************/ + +#if !defined(__FTPLIB_H) +#define __FTPLIB_H + +//#define __unix__ + +/* +#if defined(__unix__) || defined(VMS) +#define GLOBALDEF +#define GLOBALREF extern +#elif defined(_WIN32) +#if defined BUILDING_LIBRARY +#define GLOBALDEF __declspec(dllexport) +#define GLOBALREF __declspec(dllexport) +#else +#define GLOBALREF __declspec(dllimport) +#endif +#endif +*/ + +#define GLOBALDEF +#define GLOBALREF + +/* FtpAccess() type codes */ +#define FTPLIB_DIR 1 +#define FTPLIB_DIR_VERBOSE 2 +#define FTPLIB_FILE_READ 3 +#define FTPLIB_FILE_WRITE 4 + +/* FtpAccess() mode codes */ +#define FTPLIB_ASCII 'A' +#define FTPLIB_IMAGE 'I' +#define FTPLIB_TEXT FTPLIB_ASCII +#define FTPLIB_BINARY FTPLIB_IMAGE + +/* connection modes */ +#define FTPLIB_PASSIVE 1 +#define FTPLIB_PORT 2 + +/* connection option names */ +#define FTPLIB_CONNMODE 1 +#define FTPLIB_CALLBACK 2 +#define FTPLIB_IDLETIME 3 +#define FTPLIB_CALLBACKARG 4 +#define FTPLIB_CALLBACKBYTES 5 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct NetBuf netbuf; +typedef int (*FtpCallback)(netbuf *nControl, int xfered, void *arg); + +/* v1 compatibility stuff */ +#define _FTPLIB_NO_COMPAT +#if !defined(_FTPLIB_NO_COMPAT) +extern netbuf *DefaultNetbuf; + +#define ftplib_lastresp FtpLastResponse(DefaultNetbuf) +#define ftpInit FtpInit +#define ftpOpen(x) FtpConnect(x, &DefaultNetbuf) +#define ftpLogin(x,y) FtpLogin(x, y, DefaultNetbuf) +#define ftpSite(x) FtpSite(x, DefaultNetbuf) +#define ftpMkdir(x) FtpMkdir(x, DefaultNetbuf) +#define ftpChdir(x) FtpChdir(x, DefaultNetbuf) +#define ftpRmdir(x) FtpRmdir(x, DefaultNetbuf) +#define ftpNlst(x, y) FtpNlst(x, y, DefaultNetbuf) +#define ftpDir(x, y) FtpDir(x, y, DefaultNetbuf) +#define ftpGet(x, y, z) FtpGet(x, y, z, DefaultNetbuf) +#define ftpPut(x, y, z) FtpPut(x, y, z, DefaultNetbuf) +#define ftpRename(x, y) FtpRename(x, y, DefaultNetbuf) +#define ftpDelete(x) FtpDelete(x, DefaultNetbuf) +#define ftpQuit() FtpQuit(DefaultNetbuf) +#endif /* (_FTPLIB_NO_COMPAT) */ +/* end v1 compatibility stuff */ + +extern int ftplib_debug; +GLOBALREF void FtpInit(void); +GLOBALREF char *FtpLastResponse(netbuf *nControl); +GLOBALREF int FtpConnect(const char *host, netbuf **nControl); +GLOBALREF int FtpOptions(int opt, long val, netbuf *nControl); +GLOBALREF int FtpLogin(const char *user, const char *pass, netbuf *nControl); +GLOBALREF int FtpAccess(const char *path, int typ, int mode, netbuf *nControl, + netbuf **nData); +GLOBALREF int FtpRead(void *buf, int max, netbuf *nData); +GLOBALREF int FtpWrite(void *buf, int len, netbuf *nData); +GLOBALREF int FtpClose(netbuf *nData); +GLOBALREF int FtpSite(const char *cmd, netbuf *nControl); +GLOBALREF int FtpSysType(char *buf, int max, netbuf *nControl); +GLOBALREF int FtpMkdir(const char *path, netbuf *nControl); +GLOBALREF int FtpChdir(const char *path, netbuf *nControl); +GLOBALREF int FtpCDUp(netbuf *nControl); +GLOBALREF int FtpRmdir(const char *path, netbuf *nControl); +GLOBALREF int FtpPwd(char *path, int max, netbuf *nControl); +GLOBALREF int FtpNlst(const char *output, const char *path, netbuf *nControl); +GLOBALREF int FtpDir(const char *output, const char *path, netbuf *nControl); +GLOBALREF int FtpSize(const char *path, int *size, char mode, netbuf *nControl); +GLOBALREF int FtpModDate(const char *path, char *dt, int max, netbuf *nControl); +GLOBALREF int FtpGet(const char *output, const char *path, char mode, + netbuf *nControl); +GLOBALREF int FtpPut(const char *input, const char *path, char mode, + netbuf *nControl); +GLOBALREF int FtpRename(const char *src, const char *dst, netbuf *nControl); +GLOBALREF int FtpDelete(const char *fnm, netbuf *nControl); +GLOBALREF void FtpQuit(netbuf *nControl); + +static int FtpSendCmd(const char *cmd, char expresp, netbuf *nControl); + +GLOBALREF int FtpAccessBuf(const char *path, int typ, int mode, netbuf *nControl, + netbuf **nData); +GLOBALREF int FtpDirBuf(char *buf, int size, const char *path, netbuf *nControl); + +#ifdef __cplusplus +}; +#endif + +#endif /* __FTPLIB_H */ diff --git a/arm9/source/hardware.cpp b/arm9/source/hardware.cpp index 800eb7c..1dd3863 100644 --- a/arm9/source/hardware.cpp +++ b/arm9/source/hardware.cpp @@ -29,12 +29,14 @@ #include #include #include +#include #include #include #include "display.h" #include "dsCard.h" +#include "ftplib.h" #include "gba.h" #include "hardware.h" @@ -50,6 +52,11 @@ uint32 boot; bool flash_card = true; // the homebrew will always start with a FC inserted bool with_infrared = false; // ... which does not have an IR device +extern char ftp_ip[16]; +extern char ftp_user[64]; +extern char ftp_pass[64]; +extern int ftp_port; + u8 data[0x8000]; // --------------------------------------------------------------------- @@ -134,6 +141,7 @@ void hwBackup3in1() // Dump save and write it to NOR chip_reset(); OpenNorWrite(); + SetSerialMode(); displayPrintState("Writing save to NOR"); if (size < 15) size_blocks = 1; @@ -159,8 +167,6 @@ void hwBackup3in1() WriteSram(0x0a000000, (u8*)&data2, sizeof(data2)); char txt[128]; sprintf(txt, "%.12s", &nds.gameTitle[0]); - //memcpy(&txt[0], &nds.gameTitle[0], 12); - //displayPrintState(txt); displayMessage("Save has been written to 3in1.\nPlease power off and restart\nthis tool."); @@ -175,8 +181,8 @@ void hwDump3in1(uint32 size, const char *gamename) char path[256]; char fname[256] = ""; - //fileSelect("/", path, fname/*, true*/); - //if (!fname[0]) { + fileSelect("/", path, fname, 0, true, false); + if (!fname[0]) { uint32 cnt = 0; sprintf(fname, "/%s.%i.sav", gamename, cnt); while (fileExists(fname)) { @@ -188,14 +194,14 @@ void hwDump3in1(uint32 size, const char *gamename) } sprintf(fname, "/%s.%i.sav", gamename, cnt); } - //} + } char fullpath[512]; - //sprintf(fullpath, "%s/%s", path, fname); - sprintf(fullpath, "%s", fname); - displayMessage(fullpath); - + sprintf(fullpath, "%s/%s", path, fname); + //sprintf(fullpath, "%s", fname); + displayMessage(fname); + + // TODO: add support for smaller files! FILE *file = fopen(fullpath, "wb"); - //u8 *data = (u8*)malloc(0x8000); if (size < 15) size_blocks = 1; else @@ -205,8 +211,6 @@ void hwDump3in1(uint32 size, const char *gamename) u32 LEN = 0x8000; ReadNorFlash(data, i << 15, LEN); fwrite(data, 1, LEN, file); - //if (j != LEN) - //iprintf ("WRITE ERROR %i\n", errno); } //free(data); fclose(file); @@ -219,8 +223,9 @@ void hwRestore3in1() { char path[256]; char fname[256]; + memset(fname, 0, 256); displayMessage("Please select a .sav file."); - fileSelect("/", path, fname); + fileSelect("/", path, fname, 0, false, false); displayMessage(""); char msg[256]; sprintf(msg, "%s%s", path, fname); @@ -310,11 +315,9 @@ void hwRestore3in1_b(uint32 size_file) } LEN = 1 << shift; num_blocks = 1 << (size - shift); - //data = (u8*)malloc(LEN); chip_reset(); OpenNorWrite(); - SetSerialMode(); for (unsigned int i = 0; i < num_blocks; i++) { if (i % (num_blocks >> 6) == 0) displayProgressBar(i+1, num_blocks); @@ -330,13 +333,224 @@ void hwRestore3in1_b(uint32 size_file) while (1) {}; } +// ------------------------------------------------------------ void hwErase() { displayMessage("This will WIPE OUT your entire\nsave! ARE YOU SURE?\n\nPress R+up+Y to confim!"); while (!(keysCurrent() & (KEY_UP | KEY_R | KEY_Y))) {}; auxspi_erase(); displayMessage("Done! Your save is gone!"); - while(0); + while(1); +} + +// ------------------------------------------------------------ +void hwBackupFTP() +{ + netbuf *buf, *ndata; + int j; + static int jmax = 10; + + // Dump save and write it to FTP server + // First: swap card + swap_cart(); + displayPrintUpper(); + uint8 size = auxspi_save_size_log_2(); + int size_blocks = 1 << max(0, (int8(size) - 18)); // ... in units of 0x40000 bytes - that's 256 kB + uint8 type = auxspi_save_type(); + if (size < 15) + size_blocks = 1; + else + size_blocks = 1 << (uint8(size) - 15); + + // Second: connect to FTP server + displayPrintState("FTP: connecting to AP"); + if (!Wifi_InitDefault(true)) { + displayPrintState("FTP: ERROR: AP not found"); + while(1); + } + displayPrintState("FTP: connecting to FTP server"); + char fullname[512]; + sprintf(fullname, "%s:%i", ftp_ip, ftp_port); + j = 0; + while (!FtpConnect(fullname, &buf)) { + j++; + if (j >= jmax) { + displayPrintState("FTP: ERROR: FTP server missing"); + while(1); + } + swiDelay(10000); + } + displayPrintState("FTP: login"); + j = 0; + while (!FtpLogin(ftp_user, ftp_pass, buf)) { + j++; + if (j >= jmax) { + displayPrintState("FTP: ERROR: login failed"); + while(1); + } + swiDelay(10000); + } + + char fdir[256] = ""; + char fname[256] =""; + memset(fname, 0, 256); + displayMessage("Please select a file name to\noverwrite, or press L+R in a\nfolder to create a new file."); + displayPrintState("FTP: dir"); + fileSelect("/", fdir, fname, buf, true, false); + bool newfile; + if (!fname[0]) + newfile = true; + else + newfile = false; + + // Third: get a new target filename + FtpChdir(fdir, buf); + if (!fname[0]) { + displayMessage("Looking for an unused filename"); + sNDSHeader nds; + cardReadHeader((u8*)&nds); + uint32 cnt = 0; + int tsize = 0; + sprintf(fname, "%.12s.%i.sav", nds.gameTitle, cnt); + while (FtpSize(fname, &tsize, FTPLIB_IMAGE, buf) != 0) { + if (cnt < 65536) + cnt++; + else { + displayMessage("Unable to get a filename!\nThis means that you have more\nthan 65536 saves! (wow!)\nOops!"); + while(1); + } + sprintf(fname, "%.12s.%i.sav", nds.gameTitle, cnt); + displayPrintState(fname); + } + } + + // Fourth: dump save + displayPrintState(""); + sprintf(fullname, "Writing file:\n%s%s", fdir, fname); + displayMessage(fullname); + FtpAccess(fname, FTPLIB_FILE_WRITE, FTPLIB_IMAGE, buf, &ndata); + u32 length = 0x200; + if (size < 9) + length = 1 << size; + for (int i = 0; i < (1 << (size - 9)); i++) { + displayProgressBar(i+1, (size_blocks << 6)); + auxspi_read_data(i << 9, (u8*)&data[0], length, type); + int out; + if ((out = FtpWrite((u8*)&data[0], length, ndata)) < length) { + char dings[512]; + sprintf(dings, "Error: wrote %i, got %i", length, out); + displayPrintState(dings); + while(1); + } + } + FtpClose(ndata); + FtpQuit(ndata); + + Wifi_DisconnectAP(); + + displayMessage("Done! Please turn off your DS."); + while(1); +} + +void hwRestoreFTP() +{ + netbuf *buf, *ndata; + int j; + static int jmax = 10; + + // Dump save and write it to FTP server + // First: connect to FTP server + displayPrintState("FTP: connecting to AP"); + if (!Wifi_InitDefault(true)) { + displayPrintState("FTP: ERROR: AP not found"); + while(1); + } + displayPrintState("FTP: connecting to FTP server"); + char fullname[512]; + sprintf(fullname, "%s:%i", ftp_ip, ftp_port); + j = 0; + while (!FtpConnect(fullname, &buf)) { + j++; + if (j >= jmax) { + displayPrintState("FTP: ERROR: FTP server missing"); + while(1); + } + swiDelay(10000); + } + displayPrintState("FTP: login"); + j = 0; + while (!FtpLogin(ftp_user, ftp_pass, buf)) { + j++; + if (j >= jmax) { + displayPrintState("FTP: ERROR: login failed"); + while(1); + } + swiDelay(10000); + } + + // Second: select a filename + char fdir[256] = ""; + char fname[256] =""; + memset(fname, 0, 256); + displayMessage("Please select a file name to\nrestore."); + displayPrintState("FTP: dir"); + fileSelect("/", fdir, fname, buf, true, false); + /*char fn[512]; + displayMessage(fname); + while(1);*/ + + // Third: swap card + swap_cart(); + displayPrintUpper(); + uint8 size = auxspi_save_size_log_2(); + int size_blocks = 1 << (size - 9); // ... in units of 512 bytes + uint8 type = auxspi_save_type(); + if (type == 3) { + displayPrintState("Formating Flash chip"); + auxspi_erase(); + } + + displayPrintState(""); + FtpChdir(fdir, buf); + sprintf(fullname, "Reading file:\n%s%s", fdir, fname); + displayMessage(fullname); + FtpAccess(fname, FTPLIB_FILE_READ, FTPLIB_IMAGE, buf, &ndata); + u32 LEN = 0, num_blocks = 0, shift = 0; + switch (type) { + case 1: + shift = 4; // 16 bytes + break; + case 2: + shift = 5; // 32 bytes + break; + case 3: + shift = 8; // 256 bytes + break; + default: + return; + } + LEN = 1 << shift; + int num_blocks_ftp = 1 << (size - 9); + num_blocks = 1 << (size - shift); + for (int i = 0; i < num_blocks_ftp; i++) { + displayProgressBar(i+1, num_blocks_ftp); + int out; + if ((out = FtpRead((u8*)data, 512, ndata)) < 512) { + char dings[512]; + sprintf(dings, "Error: requested 512, got %i", out); + while(1); + } + for (int j = 0; j < 1 << (9-shift); j++) { + auxspi_write_data((i << 9)+(j << shift), ((u8*)data)+(j< +#include +#include +#include "iniheadings.h" + +#if defined(HAVE_STRINGS_H) +# include +#endif + +#ifndef HAVE_STRCASECMP +# define strcasecmp stricmp +#endif + + +/******************************************************************************************************************** + * Function : __ini_addHeading + * Parameters : ini - pointer to ini file database. heading - heading name + * Returns : Pointer to new heading. + * Globals Used : + * Globals Modified : + * Description : Adds a new heading to the ini file database and updates the temporary workfile + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +struct section_tag *__ini_addHeading (ini_t *ini, char *heading) +{ + struct section_tag *section; + size_t length; + long pos; + + // Format heading for storing + __ini_strtrim (heading); + + /* Create a backup of the file we are about to edit */ + if (ini->heading == heading) + return __ini_locateHeading (ini, heading); + + // Add new heading to work file and read it in + // using file add + fseek (ini->ftmp, 0, SEEK_END); + fputs ("\n[", ini->ftmp); + pos = ftell (ini->ftmp); + fputs (heading, ini->ftmp); + length = (size_t) (ftell (ini->ftmp) - pos); + section = __ini_faddHeading (ini, ini->ftmp, pos, length); + fseek (ini->ftmp, 0, SEEK_END); + fputs ("]\n", ini->ftmp); + ini->heading = section->heading; + if (section) + ini->flags |= INI_MODIFIED; + return section; +} + + +/******************************************************************************************************************** + * Function : __ini_faddheading + * Parameters : ini - pointer to ini file database, file - ini file to read heading from + * : pos - heading position in file, length - heading length + * Returns : Pointer to new heading. + * Globals Used : + * Globals Modified : + * Description : Adds a new heading to the ini file database from the input file. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +struct section_tag *__ini_faddHeading (ini_t *ini, FILE *file, long pos, size_t length) +{ + struct section_tag *section; + char *str; + + if (length) + { + length++; + str = (char *) malloc (sizeof(char) * length); + assert (str); + fseek (file, pos, SEEK_SET); + fgets (str, (int) length, file); + __ini_strtrim (str); + } + else + { + str = (char *) malloc (sizeof(char)); + assert (str); + *str = '\0'; + } + + section = __ini_createHeading (ini, str); + // Make sure heading was created + if (!section) + { + free (str); + return NULL; + } + + return section; +} + + +/******************************************************************************************************************** + * Function : __ini_createHeading + * Parameters : ini - pointer to ini file database. heading - heading name + * Returns : Pointer to new heading. + * Globals Used : + * Globals Modified : + * Description : Adds an entry into the heading linked list ready for formating by the addHeading commands + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +struct section_tag *__ini_createHeading (ini_t *ini, char *heading) +{ + struct section_tag *pNew; + + pNew = __ini_locateHeading (ini, heading); + // Check to see if heading already exists + if (pNew) + free (heading); + else + { // Create a new heading as dosen't exist + pNew = (struct section_tag *) malloc (sizeof (struct section_tag)); + if (!pNew) + return NULL; + memset (pNew, 0, sizeof (struct section_tag)); + pNew->heading = heading; + + if (*heading) + { // Found a named heading + pNew->pPrev = ini->last; + ini->last = pNew; + if (pNew->pPrev) + pNew->pPrev->pNext = pNew; + else + ini->first = pNew; + } + else + { // Anonymous heading (special case), + // always comes first + pNew->pNext = ini->first; + ini->first = pNew; + if (pNew->pNext) + pNew->pNext->pPrev = pNew; + else + ini->last = pNew; + } + +#ifdef INI_USE_HASH_TABLE + { // Rev 1.3 - Added + struct section_tag *pOld; + unsigned long crc32; + unsigned char accel; + + crc32 = __ini_createCrc32 (heading, (ini->flags & INI_CASE) != 0); + pNew->crc = crc32; + // Rev 1.3 - Add accelerator list + accel = (unsigned char) crc32 & 0x0FF; + pNew->pPrev_Acc = NULL; + pOld = ini->sections[accel]; + ini->sections[accel] = pNew; + if (pOld) pOld->pPrev_Acc = pNew; + pNew->pNext_Acc = pOld; + } +#endif // INI_USE_HASH_TABLE + } + + ini->selected = pNew; + return pNew; +} + + +/******************************************************************************************************************** + * Function : __ini_deleteHeading + * Parameters : ini - pointer to ini file database. heading - heading name + * Returns : + * Globals Used : + * Globals Modified : + * Description : Removes a heading from the database only. + * : This change does not occur in the file until ini_close is called. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +void __ini_deleteHeading (ini_t *ini) +{ + struct section_tag *current_h; + + // Delete Heading + current_h = ini->selected; + if (current_h) + { // Delete Keys + while (current_h->first) + { + current_h->selected = current_h->first; + __ini_deleteKey (ini); + } + + // Tidy up all users of this heading + ini->selected = NULL; + if (ini->last == current_h) + ini->last = current_h->pPrev; + + // Break heading out of list + if (!current_h->pPrev) + ini->first = current_h->pNext; + else + current_h->pPrev->pNext = current_h->pNext; + if (current_h->pNext) + current_h->pNext->pPrev = current_h->pPrev; + +#ifdef INI_USE_HASH_TABLE + // Rev 1.3 - Take member out of accelerator list + if (!current_h->pPrev_Acc) + ini->sections[(unsigned char) current_h->crc & 0x0FF] = current_h->pNext_Acc; + else + current_h->pPrev_Acc->pNext_Acc = current_h->pNext_Acc; + if (current_h->pNext_Acc) + current_h->pNext_Acc->pPrev_Acc = current_h->pPrev_Acc; +#endif // INI_USE_HASH_TABLE + + // Delete Heading + free (current_h->heading); + free (current_h); + } +} + + +/******************************************************************************************************************** + * Function : __ini_locateHeading + * Parameters : ini - pointer to ini file database. heading - heading name + * Returns : Pointer to heading. + * Globals Used : + * Globals Modified : + * Description : Locates a heading entry in the database. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +struct section_tag *__ini_locateHeading (ini_t *ini, const char *heading) +{ + struct section_tag *current_h; + +#ifdef INI_USE_HASH_TABLE + // Rev 1.3 - Revised to use new accelerator + unsigned long crc32; + crc32 = __ini_createCrc32 (heading, (ini->flags & INI_CASE) != 0); + + // Search for heading + for (current_h = ini->sections[(unsigned char) crc32 & 0x0FF]; current_h; current_h = current_h->pNext_Acc) + { + if (current_h->crc == crc32) + { + if (ini->flags & INI_CASE) + { + if (!strcmp (current_h->heading, heading)) + break; + } + else if (!strcasecmp (current_h->heading, heading)) + break; + } + } +#else + // Search for heading + for (current_h = ini->first; current_h; current_h = current_h->pNext) + { + if (ini->flags & INI_CASE) + { + if (!strcmp (current_h->heading, heading)) + break; + } + else if (!strcasecmp (current_h->heading, heading)) + break; + } +#endif // INI_USE_HASH_TABLE + + ini->selected = current_h; + return current_h; +} + + +/******************************************************************************************************************** + * Function : (public) ini_deleteHeading + * Parameters : ini - pointer to ini file descriptor + * Returns : -1 for Error and 0 on success + * Globals Used : + * Globals Modified : + * Description : Equivalent Microsoft write string API call where both data & key are set to NULL. + ********************************************************************************************************************/ +int INI_LINKAGE ini_deleteHeading (ini_fd_t fd) +{ + ini_t *ini = (ini_t *) fd; + if (!ini->selected) + return -1; + // Is file read only? + if (ini->mode == INI_READ) + return -1; + // Can't delete a temporary section + if (ini->selected == &(ini->tmpSection)) + return -1; + __ini_deleteHeading (ini); + ini->flags |= INI_MODIFIED; + return 0; +} + + +/******************************************************************************************************************** + * Function : (public) ini_locateHeading + * Parameters : fd - pointer to ini file descriptor + * Returns : -1 for Error and 0 on success + * Globals Used : + * Globals Modified : + * Description : Equivalent Microsoft write string API call where both data & key are set to NULL. + ********************************************************************************************************************/ +int INI_LINKAGE ini_locateHeading (ini_fd_t fd, const char *heading) +{ + ini_t *ini = (ini_t *) fd; + struct section_tag *section; + + if (!heading) + return -1; + + section = __ini_locateHeading (ini, heading); + +#ifdef INI_ADD_LIST_SUPPORT + // Rev 1.1 - Remove buffered list + if (ini->list) + { + free (ini->list); + ini->list = NULL; + } +#endif // INI_ADD_LIST_SUPPORT + + if (section) + { + section->selected = NULL; + return 0; + } + + // Ok no section was found, but maybe the user is wanting to create a + // new one so create it temporarily and see what actually happens later + { // Remove old heading + section = &(ini->tmpSection); + if (section->heading) + free (section->heading); + + // Add new heading + section->heading = strdup (heading); + if (!section->heading) + return -1; + section->selected = NULL; + ini->selected = section; + } + return -1; +} + + +/******************************************************************************************************************** + * Function : (public) ini_currentHeading + * Parameters : fd - pointer to ini file descriptor + * Returns : NULL for Error and on success pointer to heading + * Globals Used : + * Globals Modified : + * Description : Returns currently selected heading + ********************************************************************************************************************/ +const char * INI_LINKAGE ini_currentHeading (ini_fd_t fd) +{ + ini_t *ini = (ini_t *) fd; + struct section_tag *section; + + if (!ini) + return NULL; + section = ini->selected; + if (!section) + return NULL; + return section->heading; +} diff --git a/arm9/source/ini.cpp b/arm9/source/ini.cpp new file mode 100644 index 0000000..c0ba6b7 --- /dev/null +++ b/arm9/source/ini.cpp @@ -0,0 +1,1165 @@ +/*************************************************************************** + ini.cpp - Reads and writes keys to a + ini file. + + ------------------- + begin : Fri Apr 21 2000 + copyright : (C) 2000 by Simon White + email : s_a_white@email.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +/*************************************************************************** + * $Log: ini.cpp,v $ + * Revision 1.35 2004/11/26 23:18:22 s_a_white + * Add Karel Vanroye patch (whitespace). + * + * Revision 1.34 2004/11/17 23:07:10 s_a_white + * Prevent modification of read only files + * + * Revision 1.33 2004/11/05 17:52:03 s_a_white + * malloc is in stdlib.h (malloc.h does not exist on ansi c compilers). + * + * Revision 1.32 2004/10/08 18:00:14 s_a_white + * Fixed crc generation when used with new case flag. + * + * Revision 1.31 2004/10/07 23:20:49 s_a_white + * Allow support for additional flags to indicate case sensitivity and if backup + * files should be kept. + * + * Revision 1.30 2004/06/12 09:25:11 s_a_white + * Protect ini_close/flush calls from 0 file descriptors. + * + * Revision 1.29 2004/02/06 08:02:42 s_a_white + * Strcpy is unsafe when src and dst locations overlap, use memmove instead. + * + * Revision 1.28 2003/10/04 13:46:06 s_a_white + * Fix reported memory leak that list delims were not freed. + * + * Revision 1.27 2003/06/17 19:44:21 s_a_white + * Prevent comment being extracted as part of the keys data. + * + * Revision 1.26 2003/04/26 08:45:36 s_a_white + * parser.first can be 0 for anonymous sections (should be checking against + * -1 anyway). + * + * Revision 1.25 2003/02/04 22:54:02 s_a_white + * Treat NULL comment string as no comment (same as 0 length comment string). + * + * Revision 1.24 2002/09/24 19:04:22 s_a_white + * Fixed unchecked pointers in ini_append and __ini_store. + * + * Revision 1.23 2002/09/10 08:07:30 s_a_white + * Cleanup after new file creation check to prevent use of a closed file. + * + * Revision 1.22 2002/06/15 22:51:36 s_a_white + * Re-added INI_ALLOW_COMMENT to allow comments without first having + * seen a space. + * + * Revision 1.21 2002/06/14 13:22:03 s_a_white + * Bug fix to prevent sometimes accessing a NULL pointer. + * + * Revision 1.20 2002/06/11 16:23:29 s_a_white + * __ini_process nolonger returns with section/key selected + * undefined. + * + * Revision 1.19 2002/06/11 14:11:19 s_a_white + * Prevent key=key2=data being processed as two comments. + * Made comments only legal before or after a key. + * + * Revision 1.18 2002/06/10 16:59:08 s_a_white + * Improved handling of keynames with [] and + * section headings with = characters. + * + * Revision 1.17 2002/06/06 16:32:51 s_a_white + * Multi character comments now supported. + * + * Revision 1.16 2002/02/18 19:56:35 s_a_white + * Faster CRC alogarithm for hash key generation. + * + * Revision 1.15 2002/01/15 21:08:43 s_a_white + * crc fix. + * + * Revision 1.14 2001/11/15 21:19:34 s_a_white + * Appended __ini_ to global variables and INI_ to #defines. + * + * Revision 1.13 2001/11/15 21:13:13 s_a_white + * Appended __ini_ to strtrim and createCrc32. + * + * Revision 1.12 2001/09/30 15:15:30 s_a_white + * Fixed backup file removal. + * + * Revision 1.11 2001/09/22 08:56:06 s_a_white + * Added mode support. This is used to determine how the INI file should be + * accessed. + * + * Revision 1.10 2001/08/23 19:59:18 s_a_white + * ini_append fix so not freeing wrong buffer. + * + * Revision 1.9 2001/08/17 19:23:16 s_a_white + * Added ini_append. + * + * Revision 1.8 2001/08/14 22:21:21 s_a_white + * Hash table and list removal fixes. + * + * Revision 1.7 2001/07/27 11:10:15 s_a_white + * Simplified __ini_deleteAll. + * + * Revision 1.6 2001/07/21 09:47:12 s_a_white + * Bug Fixes (thanks Andy): + * *) After a flush the key and heading are now remembered. + * *) ini_deleteAll then ini_close now correctly deletes the ini file. + * *) ini_flush with no changes no longer destroys the ini object. + * + ***************************************************************************/ + +//******************************************************************************************************************* +// Include Files +//******************************************************************************************************************* +#include +#include +#include +#include +#include + +#include "iniconfig.h" +#include "ini.h" + +#include "display.h" + +#define INI_BUFFER_SIZE (1024 * 5) + +enum +{ + INI_NEW_LINE = 1, + INI_SKIP_LINE = 2, + INI_IN_SECTION = 3, + INI_END_OF_FILE = 4, + INI_CHECK_COMMENT = 5, + INI_ALLOW_COMMENT = 8 +}; + +typedef struct +{ + long pos; + long first; + long last; + struct key_tag *key; + int state; + const char *comment; + int commentpos; +} ini_parser_t; + + +//******************************************************************************************************************* +// Function Prototypes +//******************************************************************************************************************* +static ini_t *__ini_open (const char *name, ini_mode_t mode, const char *comment, int flags); +static int __ini_close (ini_t *ini, bool flush); +static void __ini_delete (ini_t *ini); +static bool __ini_extractField (ini_t *ini, FILE *file, ini_parser_t &parser, char ch); +static int __ini_process (ini_t *ini, FILE *file, const char *comment); +inline bool __ini_processComment (ini_t *ini, FILE *file, ini_parser_t &parser); +static int __ini_store (ini_t *ini, FILE *file); + + +#ifdef INI_USE_HASH_TABLE +static const unsigned long __ini_crc32Table[0x100] = +{ + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +/******************************************************************************************************************** + * Function : __ini_createCrc32 + * Parameters : str - string, strcase - case sensitive + * : length - length in bytes of data + * Returns : + * Globals Used : + * Globals Modified : + * Description : Creates a 32 bit CRC based on the input data + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +unsigned long __ini_createCrc32 (const char *str, bool strcase) +{ + unsigned long crc = 0xffffffff; + size_t length = strlen (str); + for (size_t i = 0; i < length; i++) + { + unsigned long d = strcase ? *str++ : tolower (*str++); + crc = (crc >> 8) ^ __ini_crc32Table[(crc & 0xFF) ^ d]; + } + return (crc ^ 0xffffffff); +} +#endif // INI_USE_HASH_TABLE + + +/******************************************************************************************************************** + * Function : __ini_strtrim + * Parameters : str - string to be trimmed + * Returns : + * Globals Used : + * Globals Modified : + * Description : Removes all char deemed to be spaces from start and end of string. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +void __ini_strtrim (char *str) +{ + long first, last, len; + first = 0; + last = strlen (str); + + if (!last--) + return; + + // Clip end first + while (isspace (str[last]) && last > 0) + last--; + + // Clip beginning + while (isspace (str[first]) && (first < last)) + first++; + len = last + 1 - first; + memmove (str, str + first, len); + str[len] = '\0'; +} + + +/******************************************************************************************************************** + * Function : __ini_open + * Parameters : name - ini file to parse, mode - access permission, + * : comment - file comment character(s), flags - various operational flags (backup/case/etc) + * Returns : Pointer to ini database. + * Globals Used : + * Globals Modified : + * Description : Opens an ini data file and reads it's contents into a database + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +ini_t *__ini_open (const char *name, ini_mode_t mode, const char *comment, int flags) +{ + ini_t *ini; + FILE *file = NULL; + unsigned long length; + + if (!name) + return 0; + + length = strlen(name); + if (!length) + return 0; + + // Create ini database stub + ini = (ini_t *) malloc (sizeof (ini_t)); + if (!ini) + goto ini_openError; + memset (ini, 0, sizeof (ini_t)); + + // Store ini filename + ini->filename = strdup (name); + if (!ini->filename) + goto ini_openError; + + // Open input file + ini->mode = mode; + ini->flags = flags; + file = fopen (ini->filename, "rb"); + if (!file) + { // File doesn't exist so check if allowed + // to create new one + if (mode != INI_NEW) + goto ini_openError; + + // Seems we can make a new one, check and + // make sure + file = fopen (ini->filename, "wb"); + if (!file) + goto ini_openError; + ini->flags |= INI_NEWFILE; + fclose (file); + file = NULL; + } + + // Open backup file + if (ini->mode == INI_READ) + { + // this DOES NOT WORK with devkitpro! use a different approach instead! + //ini->ftmp = tmpfile (); + ini->ftmp = fopen("/tmpfile", "wb+"); + } + else + { + ini->filename[length - 1] = '~'; + ini->ftmp = fopen (ini->filename, "wb+"); + ini->filename[length - 1] = name[length - 1]; + } + + if (!ini->ftmp) + goto ini_openError; + if (file) + { // Process existing ini file + if (__ini_process (ini, file, comment) < 0) + goto ini_openError; + fclose (file); + } +return ini; + +ini_openError: + if (ini) + { + if (ini->ftmp) + { // Close and remove backup file + fclose (ini->ftmp); + if (ini->mode != INI_READ) + { + ini->filename[strlen (ini->filename) - 1] = '~'; + remove (ini->filename); + } + } + if (ini->filename) + free (ini->filename); + free (ini); + } + + if (file) + fclose (file); + + return 0; +} + + +/******************************************************************************************************************** + * Function : __ini_close + * Parameters : ini - pointer to ini file database. force - if true, the database will be removed from + * : memory even if an error occured in saving the new ini file. + * Returns : -1 on error, 0 on success + * Globals Used : + * Globals Modified : + * Description : Save any changes back to the new ini file. + * : The backup file contains all the orignal data + any modifcations appended at the bottom + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int __ini_close (ini_t *ini, bool flush) +{ + FILE *file; + int ret = 0; + + // Open output file + if (ini->flags & INI_MODIFIED) + { + if (!ini->first) + remove(ini->filename); + else + { +#ifdef INI_ADD_LIST_SUPPORT + char *delims; + // Rev 1.1 Added - Must remove delims before saving + delims = ini->listDelims; + ini->listDelims = NULL; +#endif // INI_ADD_LIST_SUPPORT + + // Not point writing an unchanged file + file = fopen (ini->filename, "w"); + if (file) + { // Output all new headers and keys + ret = __ini_store (ini, file); + fflush (file); + fclose (file); + } + +#ifdef INI_ADD_LIST_SUPPORT + // Rev 1.1 Added - This was only a flush, so lets restore + // the old delims + ini->listDelims = delims; +#endif // INI_ADD_LIST_SUPPORT + if (!file) + return -1; + } + } + + // Check if the user dosent want the file closed. + if (!flush) + return ret; + + // Cleanup + fclose (ini->ftmp); + + if (ini->mode != INI_READ) + { // If no mods were made, delete tmp file + if ((ini->flags & (INI_MODIFIED | INI_NEWFILE | INI_BACKUP)) ^ (INI_MODIFIED | INI_BACKUP)) + { + ini->filename[strlen (ini->filename) - 1] = '~'; + remove (ini->filename); + } + } + + __ini_delete (ini); + free (ini->filename); + + if (ini->tmpSection.heading) + free (ini->tmpSection.heading); + if (ini->tmpKey.key) + free (ini->tmpKey.key); + +#ifdef INI_ADD_LIST_SUPPORT + // Rev 1.1 - Remove buffered list + __ini_listDelims (ini, NULL); +#endif // INI_ADD_LIST_SUPPORT + + free (ini); + return ret; +} + + +/******************************************************************************************************************** + * Function : __ini_delete + * Parameters : ini - pointer to ini file database. + * Returns : + * Globals Used : + * Globals Modified : + * Description : Deletes the whole ini database from memory, but leaves the ini stub so the ini_close can be + * : called. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +void __ini_delete (ini_t *ini) +{ // If already deleted, don't delete it again + if (!ini->first) + return; + + // Go through all sections deleting them + while (ini->first) + { + ini->selected = ini->first; + __ini_deleteHeading (ini); + } + +#ifdef INI_ADD_LIST_SUPPORT + // Rev 1.1 - Remove buffered list + if (ini->list) + { + free (ini->list); + ini->list = NULL; + } +#endif // INI_ADD_LIST_SUPPORT + + if (ini->mode != INI_READ) + ini->flags |= INI_MODIFIED; +} + + +/******************************************************************************************************************** + * Function : __ini_extractField + * Parameters : ini - pointer to ini file database, file - ini file to read heading from + * : parser - current parser decoding state, ch - character to process + * Returns : false on error and true on success + * Globals Used : + * Globals Modified : + * Description : Process next character and extract fields as necessary + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +bool __ini_extractField (ini_t *ini, FILE *file, ini_parser_t &parser, char ch) +{ + switch (ch) + { // Check for key value + case '=': + if (parser.state != INI_IN_SECTION) + { // Make sure the key has a string content + parser.last = parser.pos; + if (parser.first >= 0) + { + if (!ini->selected) // Handle keys which are not in a section + { + if (!__ini_faddHeading (ini, file, 0, 0)) + return false; + } + + parser.key = __ini_faddKey (ini, file, parser.first, + parser.last - parser.first); + if (!parser.key) + return false; + } + parser.state = INI_CHECK_COMMENT | INI_ALLOW_COMMENT; + } + break; + + // Check for header (must start far left) + case '[': + if (parser.state == INI_NEW_LINE) + { + parser.first = parser.pos + 1; + parser.state = INI_IN_SECTION; + } + break; + + // Check for header termination + case ']': + if (parser.state == INI_IN_SECTION) + { + parser.last = parser.pos; + if (parser.first <= parser.last) // Handle [] + { + if (!__ini_faddHeading (ini, file, parser.first, + parser.last - parser.first)) + { + return false; + } + } + parser.state = INI_SKIP_LINE; + } + break; + + default: + if (parser.state == INI_NEW_LINE) + { + parser.first = parser.pos; + parser.state = INI_NONE; + } + break; + } + return true; +} + + +/******************************************************************************************************************** + * Function : __ini_processComment + * Parameters : ini - pointer to ini file database, file - ini file to read heading from + * : parser - current parser decoding state + * Returns : false on error and true on success + * Globals Used : + * Globals Modified : + * Description : Comment strings are not passed to ini_extractField. Since multi char comments are now + * : supported we need re-process it should it eventually turn out not to be a comment. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +bool __ini_processComment (ini_t *ini, FILE *file, ini_parser_t &parser) +{ + const char *p = parser.comment; + for (; parser.commentpos > 0; parser.commentpos--) + { + if (!__ini_extractField (ini, file, parser, *p++)) + return false; + parser.pos++; + } + return true; +} + + +/******************************************************************************************************************** + * Function : __ini_process + * Parameters : ini - pointer to ini file database, file - ini file to read heading from + * Returns : -1 on error and 0 on success + * Globals Used : + * Globals Modified : + * Description : Read the ini file to determine all valid sections and keys. Also stores the location of + * : the keys data for faster accessing. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int __ini_process (ini_t *ini, FILE *file, const char *comment) +{ + char *current, ch; + size_t count; + char *buffer; + ini_parser_t parser; + int pos = 0; + + if (!ini) + return -1; + if (!file) + return -1; + + // Get a read buffer + buffer = (char *) malloc (INI_BUFFER_SIZE * sizeof(char)); + if (buffer == NULL) + return -1; + + // Clear out an existing ini structure + __ini_delete (ini); + pos = 0; + parser.pos = pos; + parser.first = -1; + parser.last = -1; + parser.state = INI_NEW_LINE | INI_ALLOW_COMMENT; + parser.key = NULL; + parser.comment = comment; + parser.commentpos = 0; + + do + { + fseek (file, pos, SEEK_SET); + current = buffer; + count = fread (buffer, sizeof(char), + INI_BUFFER_SIZE, file); + + if (count <= 0) + { + if (feof (file)) + { + count = 1; + *buffer = '\x1A'; + } + } + + while (count--) + { + ch = *current++; + switch (ch) + { + // Check for end of file + case '\x1A': + parser.state = INI_END_OF_FILE; + count = 0; + goto __ini_processLineEnd; + + // Check for newline + case '\n': case '\r': case '\f': + parser.state = INI_NEW_LINE | INI_ALLOW_COMMENT; + parser.first = -1; + parser.last = -1; + __ini_processLineEnd: + if (!__ini_processComment (ini, file, parser)) + goto __ini_processError; + parser.pos = pos; + __ini_processDataEnd: + // Now know keys data length + if (parser.key) + { + parser.key->length = (size_t) (parser.pos - parser.key->pos); + parser.key = NULL; + } + break; + + case '\0': + // If we get this we are processing a binary or corrupt file. + // Drop out here, else we may do something nasty + goto __ini_processError; + + default: + switch (parser.state & ~INI_ALLOW_COMMENT) + { + case INI_SKIP_LINE: + break; + //case INI_NONE: + //case INI_IN_SECTION: + case INI_NEW_LINE: + case INI_CHECK_COMMENT: + // Check to see if comments are allowed + if (isspace (ch)) + { + parser.commentpos = 0; + parser.state |= INI_ALLOW_COMMENT; + parser.pos = pos; + break; + } + // Deliberate run on + default: + // Check for a comment + if (parser.state & INI_ALLOW_COMMENT) + { + if (ch == parser.comment[parser.commentpos]) + { + parser.commentpos++; + if (parser.comment[parser.commentpos] == '\0') + { + parser.commentpos = 0; + parser.state = INI_SKIP_LINE; + goto __ini_processDataEnd; + } + break; + } + + parser.state &= ~INI_ALLOW_COMMENT; + if (!__ini_processComment (ini, file, parser)) + goto __ini_processError; + } + + if (parser.state != INI_CHECK_COMMENT) + { + parser.pos = pos; + if (!__ini_extractField (ini, file, parser, ch)) + goto __ini_processError; + } + } + break; + } + + // Rev 1.1 Added - Exit if EOF + if (parser.state == INI_END_OF_FILE) + break; + + fputc (ch, ini->ftmp); + pos++; + if (!pos) + { + printf ("INI file is too large\n"); + __ini_delete (ini); + return -1; + } + } + } while (parser.state != INI_END_OF_FILE); + + // Don't leave a random key located. This + // also forces user to call ini_locate* + ini->selected = NULL; + free (buffer); + return 0; + +__ini_processError: + free (buffer); + __ini_delete (ini); + return -1; +} + + +/******************************************************************************************************************** + * Function : __ini_store + * Parameters : ini - pointer to ini file database, file - ini file to read heading from + * Returns : -1 on error and 0 on success + * Globals Used : + * Globals Modified : + * Description : Writes a new ini file containing all the necessary changes + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int __ini_store (ini_t *ini, FILE *file) +{ + struct section_tag *current_h, *selected_h; + struct key_tag *current_k, *selected_k = NULL; + char *str = NULL; + size_t length = 0, equal_pos = 0; + int ret = -1; + + if (!ini) + return -1; + if (!file) + return -1; + + // Backup selected heading and key + selected_h = ini->selected; + // Be carefull if nothing was previously selected + if (selected_h != NULL) + selected_k = selected_h->selected; + + current_h = ini->first; + while (current_h) + { + // Output section heading + if (*current_h->heading) + { + if (fprintf (file, "[%s]\n", current_h->heading) < 0) + goto __ini_storeError; + } + + // Output the sections keys + equal_pos = __ini_averageLengthKey (current_h); + current_k = current_h->first; + while (current_k) + { + if (((current_k->length + 1) > length) || !str) + { // Need more space + if (str) + free (str); + length = current_k->length + 1; + str = (char *) malloc (sizeof(char) * length); + if (!str) + goto __ini_storeError; + } + + { // Output key + char format[10]; + // Rev 1.1 Added - to support lining up of equals characters + sprintf (format, "%%-%lus=", (unsigned long) equal_pos); + if (fprintf (file, format, current_k->key) < 0) + goto __ini_storeError; + } + + // Output keys data (point to correct keys data) + ini->selected = current_h; + current_h->selected = current_k; + if (ini_readString ((ini_fd_t) ini, str, length) < 0) + goto __ini_storeError; + + if (fprintf (file, "%s\n", str) < 0) + goto __ini_storeError; + + current_k = current_k->pNext; + } + + current_h = current_h->pNext; + if (fprintf (file, "\n") < 0) + goto __ini_storeError; + } + ret = 0; + +__ini_storeError: + if (str) + free (str); + // Restore selected heading and key + ini->selected = selected_h; + if (selected_h != NULL) + selected_h->selected = selected_k; + return ret; +} + + + +//******************************************************************************************************************** +//******************************************************************************************************************** +// User INI File Manipulation Functions +//******************************************************************************************************************** +//******************************************************************************************************************** + + +/******************************************************************************************************************** + * Function : ini_open + * Parameters : name - ini file to create + * Returns : Pointer to ini database. + * Globals Used : + * Globals Modified : + * Description : Opens an ini data file and reads it's contents into a database + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +ini_fd_t INI_LINKAGE ini_open (const char *name, const char *mode, + const char *comment) +{ + ini_mode_t _mode; + int flags = INI_NONE; + + if (!mode) + return NULL; + // Convert mode + switch (*mode) + { + case 'r': _mode = INI_READ; break; + case 'w': _mode = INI_NEW; break; + case 'a': _mode = INI_EXIST; break; + default: return NULL; + } + + // Check for optional flags (any order but only once) + while (*++mode != '\0') + { + int f = INI_NONE; + switch (*mode) + { + case 'b': f = INI_BACKUP; break; + case 'i': f = INI_CASE; break; + default: return NULL; + } + + if (flags & f) + return NULL; + flags |= f; + } + + // NULL can also be used to disable comments + if (comment == NULL) + comment = ""; + return (ini_fd_t) __ini_open (name, _mode, comment, flags); +} + + +/******************************************************************************************************************** + * Function : ini_close + * Parameters : ini - pointer to ini file database. + * Returns : -1 on error, 0 on success + * Globals Used : + * Globals Modified : + * Description : Call close, but make sure ini object IS deleted + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_close (ini_fd_t fd) +{ + if (fd) + return __ini_close ((ini_t *) fd, true); + return -1; +} + + +/******************************************************************************************************************** + * Function : ini_flush + * Parameters : ini - pointer to ini file database. + * Returns : -1 on error, 0 on success + * Globals Used : + * Globals Modified : + * Description : Call close, but make sure ini object IS NOT deleted + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_flush (ini_fd_t fd) +{ + if (fd) + return __ini_close ((ini_t *) fd, false); + return -1; +} + + +/******************************************************************************************************************** + * Function : ini_dataLength + * Parameters : ini - pointer to ini file database. heading - heading name. key - key name + * Returns : Number of bytes to read the keys data in as a string. 1 must be added to this length + * : to cater for a NULL character. + * Globals Used : + * Globals Modified : + * Description : Number of bytes to read the keys data in as a string + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_dataLength (ini_fd_t fd) +{ + ini_t *ini = (ini_t *) fd; + struct key_tag *_key; + if (!ini) + return -1; + + // Check to make sure a section/key has + // been asked for by the user + if (!ini->selected) + return -1; + _key = ini->selected->selected; + if (!_key) + return -1; + +#ifdef INI_ADD_LIST_SUPPORT + if (ini->listDelims) + return __ini_listIndexLength (ini); +#endif + return (int) _key->length; +} + + +/******************************************************************************************************************** + * Function : ini_delete + * Parameters : ini - pointer to ini file database. + * Returns : + * Globals Used : + * Globals Modified : + * Description : Deletes the whole ini database from memory, but leaves the ini stub so the ini_close can be + * : called. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +extern "C" int INI_LINKAGE ini_delete (ini_fd_t fd) +{ + ini_t *ini = (ini_t *) fd; + if (!ini) + return -1; + // Is file read only? + if (ini->mode == INI_READ) + return -1; + __ini_delete (ini); + return 0; +} + + +#ifdef INI_ADD_EXTRAS + +/******************************************************************************************************************** + * Function : ini_append + * Parameters : fdsrc - pointer to src ini file database to copy from. + * : fddst - pointer to dst ini file database to copy to. + * Returns : + * Globals Used : + * Globals Modified : + * Description : Copies the contents of the src ini to the dst ini. The resulting ini contains both + * : headings and keys from each. Src keys will overwrite dst keys or similar names. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +extern "C" int INI_LINKAGE ini_append (ini_fd_t fddst, ini_fd_t fdsrc) +{ + struct section_tag *current_h; + struct key_tag *current_k; + struct section_tag *src_h, *dst_h; + struct key_tag *src_k = NULL, *dst_k = NULL; + char *data = NULL; + int length = 0, ret = -1; + +#ifdef INI_ADD_LIST_SUPPORT + char *delims; +#endif + + ini_t *src = (ini_t *) fdsrc; + ini_t *dst = (ini_t *) fddst; + if (!(src && dst)) + return -1; + + // Is file read only? + if (dst->mode == INI_READ) + return -1; + + // Backup selected heading and key + src_h = src->selected; + dst_h = dst->selected; + // Be carefull if nothing was previously selected + if (src_h != NULL) + src_k = src_h->selected; + if (dst_k != NULL) + dst_k = dst_h->selected; + +#ifdef INI_ADD_LIST_SUPPORT + // Remove delims for proper reads + delims = src->listDelims; + src->listDelims = NULL; +#endif + + // Go through the src ini headings + current_h = src->first; + while (current_h) + { // Locate heading in the dst + ini_locateHeading (dst, current_h->heading); + // Go through the src keys under the heading + src->selected = current_h; + current_k = current_h->first; + while (current_k) + { // Check if data buffer can hold the key + int i = current_k->length; + current_h->selected = current_k; + if (i > length) + { // Make data buffer bigger, with some spare + length = i + 10; + if (data != NULL) + free (data); + data = (char *) malloc (sizeof (char) * length); + if (data == NULL) + goto ini_appendError; + } + // Locate key in dst ini file + ini_locateKey (dst, current_k->key); + // Copy the key from src to dst ini file + if (ini_readString (src, data, length) != i) + goto ini_appendError; + if (ini_writeString (dst, data) < 0) + goto ini_appendError; + // Move to next key + current_k = current_k->pNext; + } + // Move to next heading + current_h = current_h->pNext; + } + ret = 0; + +ini_appendError: + if (data != NULL) + free (data); + +#ifdef INI_ADD_LIST_SUPPORT + // Restore delims + src->listDelims = delims; +#endif + // Restore selected headings and keys + src->selected = src_h; + dst->selected = dst_h; + if (src_h != NULL) + src_h->selected = src_k; + if (dst_h != NULL) + dst_h->selected = dst_k; + return ret; +} + +#endif // INI_ADD_EXTRAS + + +// Add Code Modules +// Add Header Manipulation Functions +#include "headings.i" +// Add Key Manipulation Functions +#include "keys.i" +// Add Supported Datatypes +#include "types.i" +// Add List Support +#ifdef INI_ADD_LIST_SUPPORT +# include "list.i" +#endif // INI_ADD_LIST_SUPPORT diff --git a/arm9/source/ini.h b/arm9/source/ini.h new file mode 100644 index 0000000..c24817b --- /dev/null +++ b/arm9/source/ini.h @@ -0,0 +1,76 @@ +/*************************************************************************** + ini.h - Ini database definition + + ------------------- + begin : Fri Apr 21 2000 + copyright : (C) 2000 by Simon White + email : s_a_white@email.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef _ini_h_ +#define _ini_h_ + +#define INI_USE_HASH_TABLE + +#include "libini.h" +#include "inikeys.h" +#include "iniheadings.h" +#ifdef INI_ADD_LIST_SUPPORT +# include "inilist.h" +#endif + +typedef enum {INI_NEW, INI_EXIST, INI_READ} ini_mode_t; + +enum +{ + INI_NONE = 0, + INI_MODIFIED = 1 << 0, + INI_NEWFILE = 1 << 1, + INI_BACKUP = 1 << 2, // Create backup file + INI_CASE = 1 << 3 // Case sensitive +}; + +// Database containing all information about an ini file. +typedef struct ini_t +{ + char *filename; + FILE *ftmp; // Temporary work file + ini_mode_t mode; // Access mode + int flags; + + struct section_tag *first; + struct section_tag *last; + struct section_tag *selected; + char *heading; // Last written section in tmp file. + struct section_tag tmpSection; + struct key_tag tmpKey; + +#ifdef INI_USE_HASH_TABLE + struct section_tag *sections[256]; +#endif + +#ifdef INI_ADD_LIST_SUPPORT + // Rev 1.1 Added - New for list accessing + char *list; // Accelerator for accessing same list (When all list read, will be freed) + char *listDelims; // list sperators + char *listIndexPtr; // current element we wish to access (will auto increment) + unsigned int listLength; + unsigned int listIndex; +#endif // INI_ADD_LIST_SUPPORT +} ini_t; + +static void __ini_strtrim (char *str); +#ifdef INI_USE_HASH_TABLE +static unsigned long __ini_createCrc32 (const char *str, bool strcase); +#endif + +#endif // _ini_h_ diff --git a/arm9/source/iniconfig.h b/arm9/source/iniconfig.h new file mode 100644 index 0000000..418b187 --- /dev/null +++ b/arm9/source/iniconfig.h @@ -0,0 +1,59 @@ +/* include/config.h. Generated by configure. */ +/* include/config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the strcasecmp function. */ +#define HAVE_STRCASECMP + +/* Define if you have the header file. */ +//#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Name of package */ +#define PACKAGE "libini" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "1.1.10" diff --git a/arm9/source/iniheadings.h b/arm9/source/iniheadings.h new file mode 100644 index 0000000..4ae82e6 --- /dev/null +++ b/arm9/source/iniheadings.h @@ -0,0 +1,55 @@ +/*************************************************************************** + headings.h - Section Heading Manipulation Functions + + ------------------- + begin : Fri Apr 21 2000 + copyright : (C) 2000 by Simon White + email : s_a_white@email.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef _headings_h_ +#define _headings_h_ + +#include +#include "ini.h" + +//******************************************************************************************************************* +// Data structure definitions +//******************************************************************************************************************* +struct keys_tag; +struct ini_t; + +// Linked list structure for holding section/heading information. +struct section_tag +{ + char *heading; + struct key_tag *first; + struct key_tag *last; + struct key_tag *selected; + struct section_tag *pNext; + struct section_tag *pPrev; + +#ifdef INI_USE_HASH_TABLE + unsigned long crc; + struct key_tag *keys[256]; + struct section_tag *pNext_Acc; + struct section_tag *pPrev_Acc; +#endif // INI_USE_HASH_TABLE +}; + +static struct section_tag *__ini_addHeading (struct ini_t *ini, char *heading); +static struct section_tag *__ini_faddHeading (struct ini_t *ini, FILE *file, long pos, size_t length); +static struct section_tag *__ini_createHeading (struct ini_t *ini, char *heading); +static void __ini_deleteHeading (struct ini_t *ini); +static struct section_tag *__ini_locateHeading (struct ini_t *ini, const char *heading); + +#endif // _headings_h_ diff --git a/arm9/source/inikeys.h b/arm9/source/inikeys.h new file mode 100644 index 0000000..a40fcb8 --- /dev/null +++ b/arm9/source/inikeys.h @@ -0,0 +1,54 @@ +/*************************************************************************** + keys.h - Key Manipulation Functions + + ------------------- + begin : Fri Apr 21 2000 + copyright : (C) 2000 by Simon White + email : s_a_white@email.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef _keys_h_ +#define _keys_h_ + +#include +#include "ini.h" + +//******************************************************************************************************************* +// Data structure definitions +//******************************************************************************************************************* +struct ini_t; +struct section_tag; + +// Linked list structure for holding key information. +struct key_tag +{ + char *key; + long pos; + size_t length; + struct key_tag *pNext; + struct key_tag *pPrev; + +#ifdef INI_USE_HASH_TABLE + unsigned long crc; + struct key_tag *pNext_Acc; + struct key_tag *pPrev_Acc; +#endif // INI_USE_HASH_TABLE +}; + +static struct key_tag *__ini_addKey (struct ini_t *ini, char *key); +static struct key_tag *__ini_faddKey (struct ini_t *ini, FILE *file, long pos, size_t length); +static struct key_tag *__ini_createKey (struct ini_t *ini, char *key); +static void __ini_deleteKey (struct ini_t *ini); +static struct key_tag *__ini_locateKey (struct ini_t *ini, const char *key); +static size_t __ini_averageLengthKey (struct section_tag *current_h); + +#endif // _keys_h_ diff --git a/arm9/source/inilist.h b/arm9/source/inilist.h new file mode 100644 index 0000000..9e0f23e --- /dev/null +++ b/arm9/source/inilist.h @@ -0,0 +1,32 @@ +/*************************************************************************** + list.h - Adds list support to ini files + + ------------------- + begin : Fri Apr 21 2000 + copyright : (C) 2000 by Simon White + email : s_a_white@email.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef _list_h_ +#define _list_h_ + +#include "ini.h" +#ifdef INI_ADD_LIST_SUPPORT + +struct ini_t; +static int __ini_listEval (struct ini_t *ini); +static char *__ini_listRead (struct ini_t *ini); +static int __ini_listIndexLength (struct ini_t *ini); +static int __ini_listDelims (struct ini_t *ini, const char *delims); + +#endif // INI_ADD_LIST_SUPPORT +#endif // _list_h_ diff --git a/arm9/source/keys.i b/arm9/source/keys.i new file mode 100644 index 0000000..c980e90 --- /dev/null +++ b/arm9/source/keys.i @@ -0,0 +1,438 @@ +/*************************************************************************** + keys.i - Key Manipulation Functions + + ------------------- + begin : Fri Apr 21 2000 + copyright : (C) 2000 by Simon White + email : s_a_white@email.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include "inikeys.h" + +#define INI_EQUALS_ALIGN 10 + +#if defined(HAVE_STRINGS_H) +# include +#endif + +#ifndef HAVE_STRCASECMP +# define strcasecmp stricmp +#endif + + +/******************************************************************************************************************** + * Function : __ini_addKey + * Parameters : ini - pointer to ini file database. key - key name + * Returns : Pointer to key. + * Globals Used : + * Globals Modified : + * Description : Adds a new key to the ini file database and updates the temporary workfile. + * : A heading operation must be called before this function. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +struct key_tag *__ini_addKey (ini_t *ini, char *key) +{ + struct key_tag *_key; + size_t length; + long pos; + + // Format heading for storing + __ini_strtrim (key); + if (!*key) + return NULL; + + // Add new key to work file and read it in + // using file add + fseek (ini->ftmp, 0, SEEK_END); + pos = ftell (ini->ftmp); + fputs (key, ini->ftmp); + length = (size_t) (ftell (ini->ftmp) - pos); + _key = __ini_faddKey (ini, ini->ftmp, pos, length); + fseek (ini->ftmp, 0, SEEK_END); + fputc ('=', ini->ftmp); + if (_key) + ini->flags |= INI_MODIFIED; + return _key; +} + + +/******************************************************************************************************************** + * Function : __ini_averageLengthKey + * Parameters : current_h - pointer to current header. + * Returns : Returns the average key length + * Globals Used : + * Globals Modified : + * Description : Finds average key length for aligning equals. + ********************************************************************************************************************/ +size_t __ini_averageLengthKey (struct section_tag *current_h) +{ +#ifdef INI_EQUALS_ALIGN + size_t equal_pos, equal_max, keylength; + size_t average = 0, count = 0; + struct key_tag *current_k; + + // Rev 1.1 Added - Line up equals characters for keys + // Calculate Average + current_k = current_h->first; + while (current_k) + { + count++; + average += strlen (current_k->key); + current_k = current_k->pNext; + } + + if (!count) + return 0; + + average /= count; + equal_pos = (equal_max = average); + +#if INI_EQUALS_ALIGN > 0 + // Work out the longest key in that range + current_k = current_h->first; + while (current_k) + { + keylength = strlen (current_k->key); + equal_max = average + INI_EQUALS_ALIGN; + + if ((equal_max > keylength) && (keylength > equal_pos)) + equal_pos = keylength; + current_k = current_k->pNext; + } +#endif // INI_EQUALS_ALIGN > 0 + return equal_pos; +#else + return 0; +#endif // INI_EQUALS_ALIGN +} + + +/******************************************************************************************************************** + * Function : __ini_faddKey + * Parameters : ini - pointer to ini file database, file - ini file to read key from + * : pos - key position in file, length - key length + * Returns : Pointer to key. + * Globals Used : + * Globals Modified : + * Description : Adds a new key to the ini file database from the input file. + * : A heading operation must be called before this function. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +struct key_tag *__ini_faddKey (ini_t *ini, FILE *file, long pos, size_t length) +{ + struct key_tag *_key; + char *str; + + length++; + str = (char *) malloc (sizeof(char) * length); + assert (str); + fseek (file, pos, SEEK_SET); + fgets (str, (int) length, file); + __ini_strtrim (str); + + _key = __ini_createKey (ini, str); + if (!_key) + { free (str); + return NULL; + } + + _key->pos = pos + (long) length; + return _key; +} + + +/******************************************************************************************************************** + * Function : __ini_createKey + * Parameters : ini - pointer to ini file database. key - key name + * Returns : Pointer to key. + * Globals Used : + * Globals Modified : + * Description : Adds an entry into the key linked list ready for formating by the addKey commands + * : A heading operation must be called before this function. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +struct key_tag *__ini_createKey (ini_t *ini, char *key) +{ + struct section_tag *section; + struct key_tag *pNew; + long pos; + + if (!*key) + return NULL; + + section = ini->selected; + pNew = __ini_locateKey (ini, key); + if (pNew) + { // Reset details of existing key + free (pNew->key); + pNew->key = key; + pos = 0; + } + else + { // Create a new key and add at end; + pNew = (struct key_tag *) malloc (sizeof (struct key_tag)); + if (!pNew) + return NULL; + memset (pNew, 0, sizeof (struct key_tag)); + pNew->key = key; + + if (!section->first) + section->first = pNew; + else + section->last->pNext = pNew; + + pNew->pPrev = section->last; + section->last = pNew; + section->selected = pNew; + +#ifdef INI_USE_HASH_TABLE + { // Rev 1.3 - Added + struct key_tag *pOld; + unsigned long crc32; + unsigned char accel; + + crc32 = __ini_createCrc32 (key, (ini->flags & INI_CASE) != 0); + pNew->crc = crc32; + // Rev 1.3 - Add accelerator list + accel = (unsigned char) crc32 & 0x0FF; + pNew->pPrev_Acc = NULL; + pOld = section->keys[accel]; + section->keys[accel] = pNew; + if (pOld) pOld->pPrev_Acc = pNew; + pNew->pNext_Acc = pOld; + } +#endif + } + + section->selected = pNew; + return pNew; +} + + +/******************************************************************************************************************** + * Function : __ini_deleteKey + * Parameters : ini - pointer to ini file database. key - key name + * Returns : + * Globals Used : + * Globals Modified : + * Description : Removes a key from the database only. + * : This change does not occur in the file until ini_close is called. + * : A heading operation must be called before this function. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +void __ini_deleteKey (ini_t *ini) +{ + struct key_tag *current_k; + struct section_tag *section = ini->selected; + + current_k = section->selected; + if (current_k) + { + // Tidy up all users of this key + section->selected = NULL; + if (section->last == current_k) + section->last = current_k->pPrev; + + // Check to see if all keys were removed + if (!current_k->pPrev) + section->first = current_k->pNext; + else + current_k->pPrev->pNext = current_k->pNext; + if (current_k->pNext) + current_k->pNext->pPrev = current_k->pPrev; + +#ifdef INI_USE_HASH_TABLE + // Rev 1.3 - Take member out of accelerator list + if (!current_k->pPrev_Acc) + section->keys[(unsigned char) current_k->crc & 0x0FF] = current_k->pNext_Acc; + else + current_k->pPrev_Acc->pNext_Acc = current_k->pNext_Acc; + if (current_k->pNext_Acc) + current_k->pNext_Acc->pPrev_Acc = current_k->pPrev_Acc; +#endif // INI_USE_HASH_TABLE + + // Delete Key + free (current_k->key); + free (current_k); + } +} + + +/******************************************************************************************************************** + * Function : __ini_locateKey + * Parameters : ini - pointer to ini file database. key - key name + * Returns : Pointer to key. + * Globals Used : + * Globals Modified : + * Description : Locates a key entry in the database. + * : A heading operation must be called before this function. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +struct key_tag *__ini_locateKey (ini_t *ini, const char *key) +{ + struct key_tag *current_k; + struct section_tag *section; + section = ini->selected; + +#ifdef INI_USE_HASH_TABLE + { // Rev 1.3 - Added + unsigned long crc32; + crc32 = __ini_createCrc32 (key, (ini->flags & INI_CASE) != 0); + + // Search for key + for (current_k = section->keys[(unsigned char) crc32 & 0x0FF]; current_k; current_k = current_k->pNext_Acc) + { + if (current_k->crc == crc32) + { + if (ini->flags & INI_CASE) + { + if (!strcmp (current_k->key, key)) + break; + } + else if (!strcasecmp (current_k->key, key)) + break; + } + } + } +#else + { // Search for key + for (current_k = section->first; current_k; current_k = current_k->pNext) + { + if (ini->flags & INI_CASE) + { + if (!strcmp (current_k->key, key)) + break; + } + else if (!strcasecmp (current_k->key, key)) + break; + } + } +#endif // INI_USE_HASH_TABLE + + section->selected = current_k; + return current_k; +} + + +/******************************************************************************************************************** + * Function : (public) ini_deleteKey + * Parameters : ini - pointer to ini file database. heading - heading name. key - key name. + * Returns : -1 for Error and 0 on success + * Globals Used : + * Globals Modified : + * Description : Equivalent Microsoft write string API call where data set to NULL + ********************************************************************************************************************/ +int INI_LINKAGE ini_deleteKey (ini_fd_t fd) +{ + ini_t *ini = (ini_t *) fd; + if (!ini->selected || !ini->selected->selected) + return -1; + // Can't delete a temporary key + if (ini->selected->selected == &(ini->tmpKey)) + return -1; + // Is file read only? + if (ini->mode == INI_READ) + return -1; + __ini_deleteKey (ini); + ini->flags |= INI_MODIFIED; + return 0; +} + + +/******************************************************************************************************************** + * Function : (public) ini_locateKey + * Parameters : fd - pointer to ini file descriptor + * Returns : -1 for Error and 0 on success + * Globals Used : + * Globals Modified : + * Description : + ********************************************************************************************************************/ +int INI_LINKAGE ini_locateKey (ini_fd_t fd, const char *key) +{ + ini_t *ini = (ini_t *) fd; + struct key_tag *_key = NULL; + + if (!key) + return -1; + if (!ini->selected) + return -1; + + // Can't search for a key in a temporary heading + if (ini->selected != &(ini->tmpSection)) + _key = __ini_locateKey (ini, key); + +#ifdef INI_ADD_LIST_SUPPORT + // Rev 1.1 - Remove buffered list + if (ini->list) + { + free (ini->list); + ini->list = NULL; + } +#endif // INI_ADD_LIST_SUPPORT + + if (_key) + return 0; + + // Ok no key was found, but maybe the user is wanting to create a + // new one so create it temporarily and see what actually happens later + { // Remove all key + _key = &(ini->tmpKey); + if (_key->key) + free (_key->key); + + // Add new key + _key->key = strdup (key); + if (!_key) + return -1; + ini->selected->selected = _key; + } + return -1; +} + + +/******************************************************************************************************************** + * Function : (public) ini_currentKey + * Parameters : fd - pointer to ini file descriptor + * Returns : NULL for Error and on success pointer to key + * Globals Used : + * Globals Modified : + * Description : + ********************************************************************************************************************/ +const char * INI_LINKAGE ini_currentKey (ini_fd_t fd) +{ + ini_t *ini = (ini_t *) fd; + struct section_tag *section; + struct key_tag *key; + + if (!ini) + return NULL; + section = ini->selected; + if (!section) + return NULL; + key = section->selected; + if (!key) + return NULL; + return key->key; +} diff --git a/arm9/source/libini.h b/arm9/source/libini.h new file mode 100644 index 0000000..46a15db --- /dev/null +++ b/arm9/source/libini.h @@ -0,0 +1,219 @@ +/*************************************************************************** + libini.h - Header file of functions to + manipulate an ini file. + ------------------- + begin : Fri Apr 21 2000 + copyright : (C) 2000 by Simon White + email : s_a_white@email.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef _libini_h_ +#define _libini_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define INI_ADD_EXTRAS +#define INI_ADD_LIST_SUPPORT + +#ifdef SWIG +%ignore ini_readString; +%include typemaps.i +%apply int *INOUT { int *value }; +%apply long *INOUT { long *value }; +%apply double *INOUT { double *value }; +%rename (ini_readString) ini_readFileToBuffer; +#define INI_EXTERN +#define INI_STATIC +#endif /* SWIG */ + +#ifdef _WINDOWS +# define INI_LINKAGE __stdcall +#else +# define INI_LINKAGE +#endif + +/* DLL building support on win32 hosts */ +#ifndef INI_EXTERN +# ifdef DLL_EXPORT /* defined by libtool (if required) */ +# define INI_EXTERN __declspec(dllexport) +# endif +# ifdef LIBINI_DLL_IMPORT /* define if linking with this dll */ +# define INI_EXTERN extern __declspec(dllimport) +# endif +# ifndef INI_EXTERN /* static linking or !_WIN32 */ +# define INI_EXTERN extern +# endif +#endif + +#ifndef INI_ADD_EXTRAS +#undef INI_ADD_LIST_SUPPORT +#endif + +/* Compatibility with future C++ code */ +#ifndef ini_fd_t +#define ini_fd_t ini_fd_t +typedef void* ini_fd_t; +#endif + + +/* Rev 1.2 Added new fuction */ +INI_EXTERN ini_fd_t INI_LINKAGE ini_open (const char *name, const char *mode, + const char *comment); +INI_EXTERN int INI_LINKAGE ini_close (ini_fd_t fd); +INI_EXTERN int INI_LINKAGE ini_flush (ini_fd_t fd); +INI_EXTERN int INI_LINKAGE ini_delete (ini_fd_t fd); + +/* Rev 1.2 Added these functions to make life a bit easier, can still be implemented + * through ini_writeString though. */ +INI_EXTERN int INI_LINKAGE ini_locateKey (ini_fd_t fd, const char *key); +INI_EXTERN int INI_LINKAGE ini_locateHeading (ini_fd_t fd, const char *heading); +INI_EXTERN int INI_LINKAGE ini_deleteKey (ini_fd_t fd); +INI_EXTERN int INI_LINKAGE ini_deleteHeading (ini_fd_t fd); + +INI_EXTERN const char * INI_LINKAGE ini_currentKey (ini_fd_t fd); +INI_EXTERN const char * INI_LINKAGE ini_currentHeading (ini_fd_t fd); + +/* Returns the number of bytes required to be able to read the key as a + * string from the file. (1 should be added to this length to account + * for a NULL character). If delimiters are used, returns the length + * of the next data element in the key to be read */ +INI_EXTERN int INI_LINKAGE ini_dataLength (ini_fd_t fd); + +/* Default Data Type Operations + * Arrays implemented to help with reading, for writing you should format the + * complete array as a string and perform an ini_writeString. */ +INI_EXTERN int INI_LINKAGE ini_readString (ini_fd_t fd, char *str, size_t size); +INI_EXTERN int INI_LINKAGE ini_writeString (ini_fd_t fd, const char *str); +INI_EXTERN int INI_LINKAGE ini_readInt (ini_fd_t fd, int *value); + + +#ifdef INI_ADD_EXTRAS + /* Read Operations */ + INI_EXTERN int INI_LINKAGE ini_readLong (ini_fd_t fd, long *value); + INI_EXTERN int INI_LINKAGE ini_readDouble (ini_fd_t fd, double *value); + INI_EXTERN int INI_LINKAGE ini_readBool (ini_fd_t fd, int *value); + + /* Write Operations */ + INI_EXTERN int INI_LINKAGE ini_writeInt (ini_fd_t fd, int value); + INI_EXTERN int INI_LINKAGE ini_writeLong (ini_fd_t fd, long value); + INI_EXTERN int INI_LINKAGE ini_writeDouble (ini_fd_t fd, double value); + INI_EXTERN int INI_LINKAGE ini_writeBool (ini_fd_t fd, int value); + + /* Extra Functions */ + INI_EXTERN int INI_LINKAGE ini_append (ini_fd_t fddst, ini_fd_t fdsrc); +#endif /* INI_ADD_EXTRAS */ + + +#ifdef INI_ADD_LIST_SUPPORT + /* Rev 1.1 Added - List Operations (Used for read operations only) + * Be warned, once delimiters are set, every key that is read will be checked for the + * presence of sub strings. This will incure a speed hit and therefore once a line + * has been read and list/array functionality is not required, set delimiters + * back to NULL. + */ + + /* Returns the number of elements in an list being seperated by the provided delimiters */ + INI_EXTERN int INI_LINKAGE ini_listLength (ini_fd_t fd); + /* Change delimiters, default "" */ + INI_EXTERN int INI_LINKAGE ini_listDelims (ini_fd_t fd, const char *delims); + /* Set index to access in a list. When read the index will automatically increment */ + INI_EXTERN int INI_LINKAGE ini_listIndex (ini_fd_t fd, unsigned long index); +#endif /* INI_ADD_LIST_SUPPORT */ + + +#ifdef SWIG +%{ +#include +#include +#define INI_STATIC static +typedef struct +{ + char *buffer; + size_t size; +} ini_buffer_t; +%} + +%inline %{ +/************************************************************* + * SWIG helper functions to create C compatible string buffers + *************************************************************/ +INI_STATIC ini_buffer_t *ini_createBuffer (unsigned int size) +{ + ini_buffer_t *b; + /* Allocate memory to structure */ + if (size == ( ((unsigned) -1 << 1) >> 1 )) + return 0; /* Size is too big */ + b = malloc (sizeof (ini_buffer_t)); + if (!b) + return 0; + + /* Allocate memory to buffer */ + b->buffer = malloc (sizeof (char) * (size + 1)); + if (!b->buffer) + { + free (b); + return 0; + } + b->size = size; + b->buffer[0] = '\0'; + + /* Returns address to tcl */ + return b; +} + +INI_STATIC void ini_deleteBuffer (ini_buffer_t *buffer) +{ + if (!buffer) + return; + free (buffer->buffer); + free (buffer); +} + +INI_STATIC int ini_readFileToBuffer (ini_fd_t fd, ini_buffer_t *buffer) +{ + if (!buffer) + return -1; + return ini_readString (fd, buffer->buffer, buffer->size + 1); +} + +INI_STATIC char *ini_getBuffer (ini_buffer_t *buffer) +{ + if (!buffer) + return ""; + return buffer->buffer; +} + +INI_STATIC int ini_setBuffer (ini_buffer_t *buffer, const char *str) +{ + size_t len; + if (!buffer) + return -1; + len = strlen (str); + if (len > buffer->size) + len = buffer->size; + + memcpy (buffer->buffer, str, len); + buffer->buffer[len] = '\0'; + return len; +} +%} +#endif /* SWIG */ + +#ifdef __cplusplus +} +#endif + +#endif /* _libini_h_ */ diff --git a/arm9/source/list.i b/arm9/source/list.i new file mode 100644 index 0000000..3ae54c7 --- /dev/null +++ b/arm9/source/list.i @@ -0,0 +1,332 @@ +/*************************************************************************** + list.i - Adds list support to ini files + + ------------------- + begin : Fri Apr 21 2000 + copyright : (C) 2000 by Simon White + email : s_a_white@email.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include "inilist.h" + +#ifdef INI_ADD_LIST_SUPPORT + +/******************************************************************************************************************** + * Function : ini_listEval + * Parameters : ini - pointer to ini file database. + * Returns : -1 for Error or the numbers of list items found + * Globals Used : + * Globals Modified : + * Description : Uses the currently specified delimiters to calculate the number of eliments in a list + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int __ini_listEval (ini_t *ini) +{ + int length, count, i, ret; + int ldelim; + char ch; + + // Remove old list + if (ini->list) + { + free (ini->list); + ini->list = NULL; + } + + // Re-evaluate with new settings + length = ini->selected->selected->length; + if (length < 0) + return -1; + if (!length) + { + ini->listIndex = 0; + ini->listLength = 0; + if (ini->selected->selected == &ini->tmpKey) + return -1; // Can't read tmpKey + return 0; + } + + // See if there are any chars which can be used to split the string into sub ones + if (!ini->listDelims) + return -1; + ldelim = (int) strlen (ini->listDelims); + + // Buffer string for faster access + ini->list = (char *) malloc (length + 1); + if (!ini->list) + return -1; + + { // Backup up delims to avoid causing problems with readString + char *delims = ini->listDelims; + ini->listDelims = NULL; + ret = ini_readString ((ini_fd_t) ini, ini->list, length + 1); + ini->listDelims = delims; + if (ret < 0) + return -1; + } + + // Process buffer string to find number of sub strings + { + char lastch = '\0'; + count = 1; + while (length) + { + length--; + ch = ini->list[length]; + for (i = 0; i < ldelim; i++) + { + if ((char) ch == ini->listDelims[i]) + { // Prevent lots of NULL strings on multiple + // whitespace + if (lastch == '\0') + { + if (isspace (ch)) + { + ch = '\0'; + break; + } + } + + // Seperate strings + ini->list[length] = (ch = '\0'); + count++; + break; + } + } + lastch = ch; + } + } + + ini->listLength = count; + ini->listIndexPtr = ini->list; + ini->listIndex = 0; + return count; +} + + +/******************************************************************************************************************** + * Function : __ini_listRead + * Parameters : ini - pointer to ini file database. + * Returns : NULL for error, string point otherwise + * Globals Used : + * Globals Modified : + * Description : Reads the indexed parameter from the list and auto + * : increments + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +char *__ini_listRead (ini_t *ini) +{ + char *p; + + // we must read an element from the list + // Rev 1.2 Changed order of these two ifs as test was performed + // sometimes before anything was read + if (!ini->list) + { + if (__ini_listEval (ini) < 0) + return NULL; + // Handle an empty list + if (!ini->listLength) + return ""; + } + + // Check to see if we are trying to get a value beyond the end of the list + if (ini->listIndex >= ini->listLength) + return NULL; + p = ini->listIndexPtr; + // Auto increment pointers to next index + ini->listIndexPtr += (strlen (ini->listIndexPtr) + 1); + ini->listIndex++; + return p; +} + + +/******************************************************************************************************************** + * Function : ini_listLength + * Parameters : ini - pointer to ini file database. heading - heading name. key - key name. + * Returns : -1 for Error or the numbers of list items found + * Globals Used : + * Globals Modified : + * Description : Uses the currently specified delimiters to calculate the number of eliments in a list + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_listLength (ini_fd_t fd) +{ + ini_t *ini = (ini_t *) fd; + + // Check to make sure a section/key has + // been asked for by the user + if (!ini->selected) + return -1; + if (!ini->selected->selected) + return -1; + + // Check to see if we have moved to a new key + if (!ini->list) + return __ini_listEval (ini); + + return ini->listLength; +} + + +/******************************************************************************************************************** + * Function : __ini_listDelims + * Parameters : ini - pointer to ini file database. delims - string of delimitor chars + * Returns : -1 for Error or 0 for success + * Globals Used : + * Globals Modified : + * Description : Sets the delimiters used for list accessing, (default delim is NULL) + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int __ini_listDelims (struct ini_t *ini, const char *delims) +{ + if (ini->listDelims) + free (ini->listDelims); + ini->listDelims = NULL; + + // Make sure we have something to copy + if (delims) + { + if (*delims) + { // Store delims for later use + ini->listDelims = strdup (delims); + if (!ini->listDelims) + return -1; + } + } + + // List will need recalculating at some point + if (ini->list) + { + free (ini->list); + ini->list = NULL; + } + return 0; +} + + +/******************************************************************************************************************** + * Function : ini_listDelims + * Parameters : fd - pointer to ini file database. delims - string of delimitor chars + * Returns : -1 for Error or 0 for success + * Globals Used : + * Globals Modified : + * Description : Sets the delimiters used for list accessing, (default delim is NULL) + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_listDelims (ini_fd_t fd, const char *delims) +{ + return __ini_listDelims ((ini_t *) fd, delims); +} + + +/******************************************************************************************************************** + * Function : ini_listIndex + * Parameters : ini - pointer to ini file database. delims - string of delimitor chars + * Returns : + * Globals Used : + * Globals Modified : + * Description : Sets the index that the next call to any of the read function will obtain (default 0) + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_listIndex (ini_fd_t fd, unsigned long index) +{ + ini_t *ini = (ini_t *) fd; + unsigned int count; + char *p; + + // Check to make sure a section/key has + // been asked for by the user + if (!ini->selected) + return -1; + if (!ini->selected->selected) + return -1; + + // Pull in new list + if (!ini->list) + { + if (__ini_listEval (ini) < 0) + return -1; + } + + // Now scroll through to required index + if (!ini->listLength) + return -1; + if (index == ini->listIndex) + return 0; + + if (index > ini->listIndex) + { // Continue search from the from current position + count = ini->listIndex; + p = ini->listIndexPtr; + } + else + { // Reset list and search from beginning + count = 0; + p = ini->list; + } + + while (count != index) + { + count++; + if (count >= ini->listLength) + return -1; + // Jump to next sub string + p += (strlen (p) + 1); + } + + ini->listIndex = count; + ini->listIndexPtr = p; + return 0; +} + + +/******************************************************************************************************************** + * Function : __ini_listIndexLength + * Parameters : ini - pointer to ini file database + * Returns : + * Globals Used : + * Globals Modified : + * Description : Returns the length the indexed sub string + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int __ini_listIndexLength (ini_t *ini) +{ + if (!ini->list) + { // No list yet. So try to get one + if (__ini_listEval (ini) < 0) + return -1; + if (!ini->listLength) + return 0; + } + + // Now return length + return strlen (ini->listIndexPtr); +} + +#endif // INI_ADD_LIST_SUPPORT diff --git a/arm9/source/main.cpp b/arm9/source/main.cpp index 13bdb3b..124bfca 100644 --- a/arm9/source/main.cpp +++ b/arm9/source/main.cpp @@ -42,13 +42,22 @@ #include "hardware.h" #include "fileselect.h" +#include "libini.h" + uint32 ezflash = 0; uint32 dstype = 0; bool gba = 0; uint32 mode = 0; +bool slot2 = false; +char ftp_ip[16] = "ftp_ip"; +char ftp_user[64] = "ftp_user"; +char ftp_pass[64] = "ftp_pass"; +int ftp_port = 0; + +char bootdir[256] = "/"; // ============================================================================ @@ -59,9 +68,45 @@ void mode_dsi() while (1); } +void mode_slot2() +{ + // use slot2 DLDI device to store data + displayPrintState(""); + displayPrintUpper(); + displayPrintLower(); + + displayMessage("This mode is DISABLED.\nPlease remove Slot-2 module\nand restart this tool."); + while(1); + + touchPosition touchXY; + while(1) { + swiWaitForVBlank(); + touchRead(&touchXY); + + // backup + if ((touchXY.py > 8*0) && (touchXY.py < 8*8)) { + //displayPrintUpper(); + //hwBackupGBA(); + } + + // restore + if ((touchXY.py > 8*8) && (touchXY.py < 8*16)) { + //displayPrintUpper(); + //hwRestoreGBA(); + } + + // erase + if ((touchXY.py > 8*16) && (touchXY.py < 8*24)) { + //displayPrintUpper(); + //hwEraseGBA(); + } + } +} + void mode_3in1() { displayPrintUpper(); + displayPrintLower(); dsCardData data2; ReadSram(0x0a000000, (u8*)&data2, sizeof(data2)); @@ -83,11 +128,6 @@ void mode_3in1() hwRestore3in1_b(size); } - // use 3in1 to buffer data - displayPrintState("3in1 mode"); - displayPrintUpper(); - displayPrintLower(); - touchPosition touchXY; while(1) { swiWaitForVBlank(); @@ -117,7 +157,44 @@ void mode_3in1() void mode_gba() { // use 3in1 to buffer data - displayPrintState("gba mode"); + displayPrintState(""); + displayPrintUpper(); + displayPrintLower(); + + displayMessage("This mode is DISABLED.\nPlease remove Slot-2 module\nand restart this tool."); + while(1); + + touchPosition touchXY; + while(1) { + swiWaitForVBlank(); + touchRead(&touchXY); + + // backup + if ((touchXY.py > 8*0) && (touchXY.py < 8*8)) { + //displayPrintUpper(); + //hwBackupGBA(); + } + + // restore + if ((touchXY.py > 8*8) && (touchXY.py < 8*16)) { + //displayPrintUpper(); + //hwRestoreGBA(); + } + + // erase + if ((touchXY.py > 8*16) && (touchXY.py < 8*24)) { + //displayPrintUpper(); + //hwEraseGBA(); + } + } +} + +void mode_wifi() +{ + displayPrintUpper(); + + // use 3in1 to buffer data + displayPrintState(""); displayPrintUpper(); displayPrintLower(); @@ -128,33 +205,24 @@ void mode_gba() // backup if ((touchXY.py > 8*0) && (touchXY.py < 8*8)) { - displayPrintUpper(); - hwBackupGBA(); - //hwBackup3in1(); + hwBackupFTP(); } // restore if ((touchXY.py > 8*8) && (touchXY.py < 8*16)) { - displayPrintUpper(); - hwRestoreGBA(); + hwRestoreFTP(); } // erase if ((touchXY.py > 8*16) && (touchXY.py < 8*24)) { + swap_cart(); displayPrintUpper(); - hwEraseGBA(); + hwErase(); } } } -void mode_wifi() -{ - // read/write to ftp server, does nothing at the moment - displayPrintState("FTP mode - unsupported!"); - while (1); -} - -int main(void) +int main(int argc, char* argv[]) { sysSetBusOwners(true, true); @@ -168,20 +236,73 @@ int main(void) while (1) {}; } - // TODO: load ini file... + // load ini file... + ini_fd_t ini = 0; + char inipath[256]; + if (argv[0]) { + char *last = strrchr(argv[0], '/'); + int len = (last - argv[0])+1; + strncpy(bootdir, argv[0], len); + sprintf(inipath, "%s/savegame_manager.ini", bootdir); + if (fileExists(inipath)) + ini = ini_open(inipath, "r", ""); + } + if (!ini) { + sprintf(inipath, "/savegame_manager.ini"); + if (fileExists(inipath)) + ini = ini_open(inipath, "r", ""); + } + if (!ini) { + displayMessage("Unable to open ini file!\nPlease make sure that it is\n1. in this apps folder, or" + "\n2. in the root folder\nIf 1. does not work, use 2."); + while (1); + } + + ini_locateHeading(ini, ""); + ini_locateKey(ini, "ftp_ip"); + ini_readString(ini, ftp_ip, 16); + ini_locateKey(ini, "ftp_user"); + ini_readString(ini, ftp_user, 64); + ini_locateKey(ini, "ftp_pass"); + ini_readString(ini, ftp_pass, 64); + ini_locateKey(ini, "ftp_port"); + ini_readInt(ini, &ftp_port); + ini_close(ini); + //displayPrintState(ftp_user); + //while (1); // Identify hardware and branch to corresponding mode displayPrintState("Identifying hardware..."); + chip_reset(); OpenNorWrite(); ezflash = ReadNorFlashID(); CloseNorWrite(); dstype = 0; // DS/DSL, no idea how to identify DSi etc. gba = gbaIsGame(); + // Try to identify slot-2 device. Try opening slot-2 root directory + // Nope, does not work: CyclopsDS mounts itself on fat1, fat2, fat3,... (didn't test more) + /* + DIR* pdir = opendir("fat2:/"); + struct dirent *pent; + if (pdir) { + pent=readdir(pdir); + if (pent) + //char fullname[256]; + displayMessage(pent->d_name); + //slot2=true; + } + */ + if (argv[0][3] == '2') + slot2 = true; + if (dstype == 1) { // DSi/DSiXL, will branch to SD-card mode when cracked. mode = 3; mode_dsi(); + } else if (slot2) { + mode = 4; + mode_slot2(); } else if (ezflash != 0) { // DS with EZFlash found -> branch to 3in1 mode mode = 2; diff --git a/arm9/source/types.i b/arm9/source/types.i new file mode 100644 index 0000000..a5ccabf --- /dev/null +++ b/arm9/source/types.i @@ -0,0 +1,530 @@ +/*************************************************************************** + types.i - Libini supported data types + Use readString for others + + ------------------- + begin : Fri Apr 21 2000 + copyright : (C) 2000 by Simon White + email : s_a_white@email.com + ***************************************************************************/ + +/*************************************************************************** + * * + * 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 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include "ini.h" + + +/******************************************************************************************************************** + * Function : __ini_read + * Parameters : ini - pointer to ini file database, size - key length returned here + * Returns : -1 for Error and 0 on success. + * Globals Used : + * Globals Modified : + * Description : Common read functionality + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +static int __ini_read (ini_t *ini, size_t *size) +{ + struct key_tag *_key; + + if (!ini->selected) + return -1; + // Locate and read keys value + _key = ini->selected->selected; + if (!_key) + return -1; + + if (_key->length) + { // Locate and read back keys data + fseek (ini->ftmp, _key->pos, SEEK_SET); + } + else if (_key == &ini->tmpKey) + return -1; // Can't read tmpKey + *size = _key->length; + return 0; +} + + +#ifdef INI_ADD_LIST_SUPPORT +/******************************************************************************************************************** + * Function : __ini_readList + * Parameters : ini - pointer to ini file database. + * Returns : Pointer to buffer holding data. + * Globals Used : + * Globals Modified : + * Description : Common read functionality from a list + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +static char *__ini_readList (ini_t *ini) +{ + if (!ini->selected) + return NULL; + // Locate and read keys value + if (!ini->selected->selected) + return NULL; + return __ini_listRead (ini); +} +#endif // INI_ADD_LIST_SUPPORT + + +/******************************************************************************************************************** + * Function : __ini_write + * Parameters : ini - pointer to ini file database. heading - heading name. key - key name. + * Returns : Pointer to new key. + * Globals Used : + * Globals Modified : + * Description : Make calls to add a new key and appends a description of changes in the backup file + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +static struct key_tag *__ini_write (ini_t *ini) +{ + struct section_tag *section; + struct key_tag *key; + + // Is file read only? + if (ini->mode == INI_READ) + return NULL; + + // Check to make sure a section/key has + // been asked for by the user + section = ini->selected; + if (!section) + return NULL; + key = section->selected; + if (!key) + return NULL; + + // Add or replace key + if (!__ini_addHeading (ini, section->heading)) + return NULL; + return __ini_addKey (ini, key->key); +} + + +/******************************************************************************************************************** + * Function : ini_readString + * Parameters : ini - pointer to ini file database. + * : value - keys data + * Returns : -1 for error or the number of chars read. + * Globals Used : + * Globals Modified : + * Description : Reads data part from a key and returns it as a string + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_readString (ini_fd_t fd, char *str, size_t size) +{ + ini_t *ini = (ini_t *) fd; + + // Check size and reserve space for NULL + if (size-- <= 0) + return -1; + +#ifdef INI_ADD_LIST_SUPPORT + if (ini->listDelims) + { + char *data = __ini_readList (ini); + if (!data) + return -1; + strncpy (str, data, size); + } + else +#endif // INI_ADD_LIST_SUPPORT + { // Locate and read back keys data (Index ignored) + // Check to make sure size is correct + size_t length; + if (__ini_read (ini, &length) < 0) + return -1; + if (size > length) + size = length; + size = fread (str, sizeof(char), size, ini->ftmp); + } + + str[size] = '\0'; + __ini_strtrim (str); + return (int) size; +} + + +/******************************************************************************************************************** + * Function : ini_writeString + * Parameters : ini - pointer to ini file database. + * : value - keys data + * Returns : -1 for Error and 0 on success + * Globals Used : + * Globals Modified : + * Description : Writes data part to a key. + * : Conforms to Microsoft API call. E.g. use NULLS to remove headings/keys + * : Headings and keys will be created as necessary + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_writeString (ini_fd_t fd, const char *str) +{ + ini_t *ini = (ini_t *) fd; + struct key_tag *_key; + + _key = __ini_write (ini); + if (!_key) + return -1; + + // Write data to bottom of backup file + _key->length = strlen (str); + fprintf (ini->ftmp, "%s\n", str); + return 0; +} + + +/******************************************************************************************************************** + * Function : ini_readInt + * Parameters : ini - pointer to ini file database. + * : value - keys data + * Returns : -1 for error or the number of values read. + * Globals Used : + * Globals Modified : + * Description : Reads data part from a key and returns it as a int + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_readInt (ini_fd_t fd, int *value) +{ + ini_t *ini = (ini_t *) fd; + int ret = 0; + +#ifdef INI_ADD_LIST_SUPPORT + if (ini->listDelims) + { + char *data = __ini_readList (ini); + if (data) + sscanf (data, "%d", value); + } + else +#endif // INI_ADD_LIST_SUPPORT + { + size_t length; + if (__ini_read (ini, &length) >= 0) + { + if (length > 0) + ret = fscanf (ini->ftmp, "%d", value); + } + } + + if (ret == 1) + return 0; + return -1; +} + + +#ifdef INI_ADD_EXTRAS + +/******************************************************************************************************************** + * Function : ini_readLong + * Parameters : ini - pointer to ini file database. + * : value - keys data + * Returns : -1 for error or the number of values read. + * Globals Used : + * Globals Modified : + * Description : Reads data part from a key and returns it as a long + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_readLong (ini_fd_t fd, long *value) +{ + ini_t *ini = (ini_t *) fd; + int ret = 0; + +#ifdef INI_ADD_LIST_SUPPORT + if (ini->listDelims) + { + char *data = __ini_readList (ini); + if (data) + ret = sscanf (data, "%ld", value); + } + else +#endif // INI_ADD_LIST_SUPPORT + { + size_t length; + if (__ini_read (ini, &length) >= 0) + { + if (length > 0) + ret = fscanf (ini->ftmp, "%ld", value); + } + } + + if (ret == 1) + return 0; + return -1; +} + + +/******************************************************************************************************************** + * Function : ini_readDouble + * Parameters : ini - pointer to ini file database. + * : value - keys data + * Returns : -1 for error or the number of values read. + * Globals Used : + * Globals Modified : + * Description : Reads data part from a key and returns it as a double (real) + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_readDouble (ini_fd_t fd, double *value) +{ + ini_t *ini = (ini_t *) fd; + int ret = 0; + +#ifdef INI_ADD_LIST_SUPPORT + if (ini->listDelims) + { + char *data = __ini_readList (ini); + if (data) + ret = sscanf (data, "%lf", value); + } + else +#endif // INI_ADD_LIST_SUPPORT + { + size_t length; + if (__ini_read (ini, &length) >= 0) + { + if (length > 0) + ret = fscanf (ini->ftmp, "%lf", value); + } + } + + if (ret == 1) + return 0; + return -1; +} + + +/******************************************************************************************************************** + * Function : ini_readBool + * Parameters : ini - pointer to ini file database. + * : value - keys data + * Returns : -1 for error or the number of values read. + * Globals Used : + * Globals Modified : + * Description : Reads data part from a key and returns it as a int. Supported bool strings are 0, 1, true + * : false. + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_readBool (ini_fd_t fd, int *value) +{ + ini_t *ini = (ini_t *) fd; + char buffer[6] = ""; + +#ifdef INI_ADD_LIST_SUPPORT + if (ini->listDelims) + { + char *data = __ini_readList (ini); + if (!data) + return -1; + sscanf (data, "%5s", buffer); + } + else +#endif // INI_ADD_LIST_SUPPORT + { + size_t length; + if (__ini_read (ini, &length) < 0) + return -1; + if (length > 0) + fscanf (ini->ftmp, "%5s", buffer); + } + + { // Convert string to lower case + char *p = buffer; + while (*p != '\0') + { + *p = (char) tolower (*p); + p++; + } + } + + // Decode supported bool types + switch (*buffer) + { + case '0': + case '1': + if (buffer[1] != '\0') + return -1; + *value = *buffer - '0'; + break; + default: + if (!strcasecmp (buffer, "true")) + *value = 1; + else if (!strcasecmp (buffer, "false")) + *value = 0; + else if (!strcasecmp (buffer, "on")) + *value = 1; + else if (!strcasecmp (buffer, "off")) + *value = 0; + else if (!strcasecmp (buffer, "yes")) + *value = 1; + else if (!strcasecmp (buffer, "no")) + *value = 0; + else // No match + return -1; + break; + } + return 0; +} + + +/******************************************************************************************************************** + * Function : ini_writeInt + * Parameters : ini - pointer to ini file database. + * : value - keys data + * Returns : -1 for Error and 0 on success + * Globals Used : + * Globals Modified : + * Description : Writes data part to a key. + * : Headings and keys will be created as necessary + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_writeInt (ini_fd_t fd, int value) +{ + ini_t *ini = (ini_t *) fd; + struct key_tag *_key; + long pos; + + _key = __ini_write (ini); + if (!_key) + return -1; + + // Write data to bottom of backup file + fprintf (ini->ftmp, "%d", value); + pos = ftell (ini->ftmp); + _key->length = (size_t) (pos - _key->pos); + fprintf (ini->ftmp, "\n"); + return 0; +} + + +/******************************************************************************************************************** + * Function : ini_writeLong + * Parameters : ini - pointer to ini file database. + * : value - keys data + * Returns : -1 for Error and 0 on success + * Globals Used : + * Globals Modified : + * Description : Writes data part to a key. + * : Headings and keys will be created as necessary + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_writeLong (ini_fd_t fd, long value) +{ + ini_t *ini = (ini_t *) fd; + struct key_tag *_key; + long pos; + + _key = __ini_write (ini); + if (!_key) + return -1; + + // Write data to bottom of backup file + fprintf (ini->ftmp, "%ld", value); + pos = ftell (ini->ftmp); + _key->length = (size_t) (pos - _key->pos); + fprintf (ini->ftmp, "\n"); + return 0; +} + + +/******************************************************************************************************************** + * Function : ini_writeDouble + * Parameters : ini - pointer to ini file database. + * : value - keys data + * Returns : -1 for Error and 0 on success + * Globals Used : + * Globals Modified : + * Description : Writes data part to a key. + * : Headings and keys will be created as necessary + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_writeDouble (ini_fd_t fd, double value) +{ + ini_t *ini = (ini_t *) fd; + struct key_tag *_key; + long pos; + + _key = __ini_write (ini); + if (!_key) + return -1; + + // Write data to bottom of backup file + fprintf (ini->ftmp, "%f", value); + pos = ftell (ini->ftmp); + _key->length = (size_t) (pos - _key->pos); + fprintf (ini->ftmp, "\n"); + return 0; +} + + +/******************************************************************************************************************** + * Function : ini_writeBool + * Parameters : ini - pointer to ini file database. + * : value - keys data + * Returns : -1 for Error and 0 on success + * Globals Used : + * Globals Modified : + * Description : Writes data part to a key. + * : Headings and keys will be created as necessary + ******************************************************************************************************************** + * Rev | Date | By | Comment + * ---------------------------------------------------------------------------------------------------------------- + ********************************************************************************************************************/ +int INI_LINKAGE ini_writeBool (ini_fd_t fd, int value) +{ + ini_t *ini = (ini_t *) fd; + struct key_tag *_key; + long pos; + + // Check if value is legal + if ((value < 0) || (value > 1)) + return -1; + + _key = __ini_write (ini); + if (!_key) + return -1; + + // Write data to bottom of backup file + if (value) + fprintf (ini->ftmp, "true"); + else + fprintf (ini->ftmp, "false"); + pos = ftell (ini->ftmp); + _key->length = (size_t) (pos - _key->pos); + fprintf (ini->ftmp, "\n"); + return 0; +} + +#endif // INI_ADD_EXTRAS diff --git a/savegame_manager.pnproj b/savegame_manager.pnproj index 67fab5d..cb84d77 100644 --- a/savegame_manager.pnproj +++ b/savegame_manager.pnproj @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/savegame_manager.pnps b/savegame_manager.pnps index 76379c6..14f54a4 100644 --- a/savegame_manager.pnps +++ b/savegame_manager.pnps @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file