/* ini.c * Copyright (C) 2002-2005 PCSX2 Team * * 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. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * * PCSX2 members can be contacted through their website at www.pcsx2.net. */ #include // NULL #include // sprintf() #include "logfile.h" #include "actualfile.h" #include "ini.h" const char INIext[] = ".ini"; const char INInewext[] = ".new"; // Returns: position where new extensions should be added. int INIRemoveExt(char *argname, char *tempname) { int i; int j; int k; #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: RemoveExt(%s)", argname); #endif /* VERBOSE_FUNCTION_INI */ i = 0; while((i <= INIMAXLEN) && (*(argname + i) != 0)) { *(tempname + i) = *(argname + i); i++; } // ENDWHILE- Copying the argument name into a temporary area; *(tempname + i) = 0; // And 0-terminate k = i; k--; j = 0; while((j <= INIMAXLEN) && (INIext[j] != 0)) j++; j--; while((j >= 0) && (*(tempname + k) == INIext[j])) { k--; j--; } // ENDWHILE- Comparing the ending characters to the INI ext. if(j < 0) { k++; i = k; *(tempname + i) = 0; // 0-terminate, cutting off ".ini" } // ENDIF- Do we have a match? Then remove the end chars. return(i); } // END INIRemoveExt() void INIAddInExt(char *tempname, int temppos) { int i; #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: AddInExt(%s, %i)", tempname, temppos); #endif /* VERBOSE_FUNCTION_INI */ i = 0; while((i + temppos < INIMAXLEN) && (INIext[i] != 0)) { *(tempname + temppos + i) = INIext[i]; i++; } // ENDWHILE- Attaching extenstion to filename *(tempname + temppos + i) = 0; // And 0-terminate } // END INIAddInExt() void INIAddOutExt(char *tempname, int temppos) { int i; #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: AddOutExt(%s, %i)", tempname, temppos); #endif /* VERBOSE_FUNCTION_INI */ i = 0; while((i + temppos < INIMAXLEN) && (INInewext[i] != 0)) { *(tempname + temppos + i) = INInewext[i]; i++; } // ENDWHILE- Attaching extenstion to filename *(tempname + temppos + i) = 0; // And 0-terminate } // END INIAddInExt() // Returns number of bytes read to get line (0 means end-of-file) int INIReadLine(ACTUALHANDLE infile, char *buffer) { int charcount; int i; char tempin[2]; int retflag; int retval; #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: ReadLine()"); #endif /* VERBOSE_FUNCTION_INI */ charcount = 0; i = 0; tempin[1] = 0; retflag = 0; while((i < INIMAXLEN) && (retflag < 2)) { retval = ActualFileRead(infile, 1, tempin); charcount++; if(retval != 1) { retflag = 2; charcount--; } else if(tempin[0] == '\n') { retflag = 2; } else if(tempin[0] >= ' ') { *(buffer + i) = tempin[0]; i++; } // ENDLONGIF- How do we react to the next character? } // ENDWHILE- Loading up on characters until an End-of-Line appears *(buffer + i) = 0; // And 0-terminate #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: Line: %s", buffer); #endif /* VERBOSE_FUNCTION_INI */ return(charcount); } // END INIReadLine() // Note: Do we need to back-skip a char if something other \n follows \r? // Returns: number of bytes to get to start of section (or -1) int INIFindSection(ACTUALHANDLE infile, char *section) { int charcount; int i; int retflag; int retval; char scanbuffer[INIMAXLEN+1]; #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: FindSection(%s)", section); #endif /* VERBOSE_FUNCTION_INI */ charcount = 0; retflag = 0; while(retflag == 0) { retval = INIReadLine(infile, scanbuffer); if(retval == 0) return(-1); // EOF? Stop here. if(scanbuffer[0] == '[') { i = 0; while((i < INIMAXLEN) && (*(section + i) != 0) && (*(section + i) == scanbuffer[i + 1])) i++; if((i < INIMAXLEN - 2) && (*(section + i) == 0)) { if((scanbuffer[i + 1] == ']') && (scanbuffer[i + 2] == 0)) { retflag = 1; } // ENDIF- End marks look good? Return successful. } // ENDIF- Do we have a section match? } // ENDIF- Does this look like a section header? if(retflag == 0) charcount += retval; } // ENDWHILE- Scanning lines for the correct [Section] header. return(charcount); } // END INIFindSection() // Returns: number of bytes to get to start of keyword (or -1) int INIFindKeyword(ACTUALHANDLE infile, char *keyword, char *buffer) { int charcount; int i; int j; int retflag; int retval; char scanbuffer[INIMAXLEN+1]; #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: FindKeyword(%s)", keyword); #endif /* VERBOSE_FUNCTION_INI */ charcount = 0; retflag = 0; while(retflag == 0) { retval = INIReadLine(infile, scanbuffer); if(retval == 0) return(-1); // EOF? Stop here. if(scanbuffer[0] == '[') return(-1); // New section? Stop here. i = 0; while((i < INIMAXLEN) && (*(keyword + i) != 0) && (*(keyword + i) == scanbuffer[i])) i++; if((i < INIMAXLEN - 2) && (*(keyword + i) == 0)) { if(scanbuffer[i] == '=') { retflag = 1; if(buffer != NULL) { i++; j = 0; while((i < INIMAXLEN) && (scanbuffer[i] != 0)) { *(buffer + j) = scanbuffer[i]; i++; j++; } // ENDWHILE- Copying the value out to the outbound buffer. *(buffer + j) = 0; // And 0-terminate. } // ENDIF- Return the value as well? } // ENDIF- End marks look good? Return successful. } // ENDIF- Do we have a section match? if(retflag == 0) charcount += retval; } // ENDWHILE- Scanning lines for the correct [Section] header. #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: Value: %s", buffer); #endif /* VERBOSE_FUNCTION_INI */ return(charcount); } // END INIFindKeyWord() // Returns: number of bytes left to write... (from charcount back) int INICopy(ACTUALHANDLE infile, ACTUALHANDLE outfile, int charcount) { char buffer[4096]; int i; int chunk; int retval; #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: Copy(%i)", charcount); #endif /* VERBOSE_FUNCTION_INI */ i = charcount; chunk = 4096; if(i < chunk) chunk = i; while(chunk > 0) { retval = ActualFileRead(infile, chunk, buffer); if(retval <= 0) return(i); // Trouble? Stop here. if(retval < chunk) chunk = retval; // Short block? Note it. retval = ActualFileWrite(outfile, chunk, buffer); if(retval <= 0) return(i); // Trouble? Stop here. i -= retval; if(retval < chunk) return(i); // Short block written? Stop here. chunk = 4096; if(i < chunk) chunk = i; } // ENDWHILE- Copying a section of file across, one chunk at a time. return(0); } // END INICopyToPos() int INISaveString(char *file, char *section, char *keyword, char *value) { char inname[INIMAXLEN+1]; char outname[INIMAXLEN+1]; int filepos; ACTUALHANDLE infile; ACTUALHANDLE outfile; int i; int retval; char templine[INIMAXLEN+1]; if(file == NULL) return(-1); if(section == NULL) return(-1); if(keyword == NULL) return(-1); if(value == NULL) return(-1); #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: SaveString(%s, %s, %s, %s)", file, section, keyword, value); #endif /* VERBOSE_FUNCTION_INI */ filepos = INIRemoveExt(file, inname); for(i = 0; i <= filepos; i++) outname[i] = inname[i]; INIAddInExt(inname, filepos); INIAddOutExt(outname, filepos); filepos = 0; infile = ActualFileOpenForRead(inname); if(infile == ACTUALHANDLENULL) { #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: creating new file"); #endif /* VERBOSE_FUNCTION_INI */ outfile = ActualFileOpenForWrite(inname); if(outfile == ACTUALHANDLENULL) return(-1); // Just a bad name? Abort. sprintf(templine, "[%s]\r\n", section); i = 0; while((i < INIMAXLEN) && (templine[i] != 0)) i++; retval = ActualFileWrite(outfile, i, templine); if(retval < i) { ActualFileClose(outfile); outfile = ACTUALHANDLENULL; ActualFileDelete(inname); return(-1); } // ENDIF- Trouble writing it out? Abort. sprintf(templine, "%s=%s\r\n", keyword, value); i = 0; while((i < INIMAXLEN) && (templine[i] != 0)) i++; retval = ActualFileWrite(outfile, i, templine); ActualFileClose(outfile); outfile = ACTUALHANDLENULL; if(retval < i) { ActualFileDelete(inname); return(-1); } // ENDIF- Trouble writing it out? Abort. return(0); } // ENDIF- No input file? Create a brand new .ini file then. retval = INIFindSection(infile, section); if(retval < 0) { #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: creating new section"); #endif /* VERBOSE_FUNCTION_INI */ outfile = ActualFileOpenForWrite(outname); if(outfile == ACTUALHANDLENULL) { ActualFileClose(infile); infile = ACTUALHANDLENULL; return(-1); } // ENDIF- Couldn't open a temp file? Abort ActualFileSeek(infile, 0); // Move ini to beginning of file... INICopy(infile, outfile, 0x0FFFFFFF); // Copy the whole file out... sprintf(templine, "\r\n[%s]\r\n", section); i = 0; while((i < INIMAXLEN) && (templine[i] != 0)) i++; retval = ActualFileWrite(outfile, i, templine); if(retval < i) { ActualFileClose(infile); infile = ACTUALHANDLENULL; ActualFileClose(outfile); outfile = ACTUALHANDLENULL; ActualFileDelete(outname); return(-1); } // ENDIF- Trouble writing it out? Abort. sprintf(templine, "%s=%s\r\n", keyword, value); i = 0; while((i < INIMAXLEN) && (templine[i] != 0)) i++; retval = ActualFileWrite(outfile, i, templine); ActualFileClose(infile); infile = ACTUALHANDLENULL; ActualFileClose(outfile); outfile = ACTUALHANDLENULL; if(retval < i) { ActualFileDelete(outname); return(-1); } // ENDIF- Trouble writing it out? Abort. ActualFileDelete(inname); ActualFileRename(outname, inname); return(0); } // ENDIF- Couldn't find the section? Make a new one! filepos = retval; ActualFileSeek(infile, filepos); filepos += INIReadLine(infile, templine); // Get section line's byte count retval = INIFindKeyword(infile, keyword, NULL); if(retval < 0) { #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: creating new keyword"); #endif /* VERBOSE_FUNCTION_INI */ ActualFileSeek(infile, filepos); retval = INIReadLine(infile, templine); i = 0; while((i < INIMAXLEN) && (templine[i] != 0) && (templine[i] != '=')) i++; while((retval > 0) && (templine[i] == '=')) { filepos += retval; retval = INIReadLine(infile, templine); i = 0; while((i < INIMAXLEN) && (templine[i] != 0) && (templine[i] != '=')) i++; } // ENDWHILE- skimming to the bottom of the section outfile = ActualFileOpenForWrite(outname); if(outfile == ACTUALHANDLENULL) { ActualFileClose(infile); infile = ACTUALHANDLENULL; return(-1); } // ENDIF- Couldn't open a temp file? Abort ActualFileSeek(infile, 0); retval = INICopy(infile, outfile, filepos); if(retval > 0) { ActualFileClose(infile); infile = ACTUALHANDLENULL; ActualFileClose(outfile); outfile = ACTUALHANDLENULL; ActualFileDelete(outname); return(-1); } // ENDIF- Trouble writing everything up to keyword? Abort. sprintf(templine, "%s=%s\r\n", keyword, value); i = 0; while((i < INIMAXLEN) && (templine[i] != 0)) i++; retval = ActualFileWrite(outfile, i, templine); if(retval < i) { ActualFileClose(infile); infile = ACTUALHANDLENULL; ActualFileClose(outfile); outfile = ACTUALHANDLENULL; ActualFileDelete(outname); return(-1); } // ENDIF- Trouble writing it out? Abort. } else { #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: replacing keyword"); #endif /* VERBOSE_FUNCTION_INI */ filepos += retval; // Position just before old version of keyword outfile = ActualFileOpenForWrite(outname); if(outfile == ACTUALHANDLENULL) { ActualFileClose(infile); infile = ACTUALHANDLENULL; return(-1); } // ENDIF- Couldn't open a temp file? Abort ActualFileSeek(infile, 0); retval = INICopy(infile, outfile, filepos); if(retval > 0) { ActualFileClose(infile); infile = ACTUALHANDLENULL; ActualFileClose(outfile); outfile = ACTUALHANDLENULL; ActualFileDelete(outname); return(-1); } // ENDIF- Trouble writing everything up to keyword? Abort. INIReadLine(infile, templine); // Read past old keyword/value... // Replace with new value sprintf(templine, "%s=%s\r\n", keyword, value); i = 0; while((i < INIMAXLEN) && (templine[i] != 0)) i++; retval = ActualFileWrite(outfile, i, templine); if(retval < i) { ActualFileClose(infile); infile = ACTUALHANDLENULL; ActualFileClose(outfile); outfile = ACTUALHANDLENULL; ActualFileDelete(outname); return(-1); } // ENDIF- Trouble writing it out? Abort. } // ENDIF- Need to add a new keyword? INICopy(infile, outfile, 0xFFFFFFF); // Write out rest of file ActualFileClose(infile); infile = ACTUALHANDLENULL; ActualFileClose(outfile); outfile = ACTUALHANDLENULL; ActualFileDelete(inname); ActualFileRename(outname, inname); return(0); } // END INISaveString() int INILoadString(char *file, char *section, char *keyword, char *buffer) { char inname[INIMAXLEN+1]; int filepos; ACTUALHANDLE infile; int retval; if(file == NULL) return(-1); if(section == NULL) return(-1); if(keyword == NULL) return(-1); if(buffer == NULL) return(-1); #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: LoadString(%s, %s, %s)", file, section, keyword); #endif /* VERBOSE_FUNCTION_INI */ filepos = INIRemoveExt(file, inname); INIAddInExt(inname, filepos); filepos = 0; infile = ActualFileOpenForRead(inname); if(infile == ACTUALHANDLENULL) return(-1); retval = INIFindSection(infile, section); if(retval < 0) { ActualFileClose(infile); infile = ACTUALHANDLENULL; return(-1); } // ENDIF- Didn't find it? Abort. retval = INIFindKeyword(infile, keyword, buffer); if(retval < 0) { ActualFileClose(infile); infile = ACTUALHANDLENULL; return(-1); } // ENDIF- Didn't find it? Abort. ActualFileClose(infile); infile = ACTUALHANDLENULL; return(0); } // END INILoadString() int INIRemove(char *file, char *section, char *keyword) { char inname[INIMAXLEN+1]; char outname[INIMAXLEN+1]; int filepos; ACTUALHANDLE infile; ACTUALHANDLE outfile; char templine[INIMAXLEN+1]; int i; int retval; if(file == NULL) return(-1); if(section == NULL) return(-1); #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: Remove(%s, %s, %s)", file, section, keyword); #endif /* VERBOSE_FUNCTION_INI */ filepos = INIRemoveExt(file, inname); for(i = 0; i <= filepos; i++) outname[i] = inname[i]; INIAddInExt(inname, filepos); INIAddOutExt(outname, filepos); infile = ActualFileOpenForRead(inname); if(infile == ACTUALHANDLENULL) return(-1); retval = INIFindSection(infile, section); if(retval == -1) { ActualFileClose(infile); infile = ACTUALHANDLENULL; return(-1); } // ENDIF- Couldn't even find the section? Abort filepos = retval; if(keyword == NULL) { #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: removing section"); #endif /* VERBOSE_FUNCTION_INI */ outfile = ActualFileOpenForWrite(outname); if(outfile == ACTUALHANDLENULL) { ActualFileClose(infile); infile = ACTUALHANDLENULL; return(-1); } // ENDIF- Couldn't open a temp file? Abort ActualFileSeek(infile, 0); retval = INICopy(infile, outfile, filepos); if(retval > 0) { ActualFileClose(infile); infile = ACTUALHANDLENULL; ActualFileClose(outfile); outfile = ACTUALHANDLENULL; ActualFileDelete(outname); return(-1); } // ENDIF- Trouble writing everything up to the section? Abort. templine[0] = 0; retval = 1; while((retval > 0) && (templine[0] != '[')) { retval = INIReadLine(infile, templine); } // ENDWHILE- Read to the start of the next section... or EOF. if(templine[0] == '[') { i = 0; while((i < INIMAXLEN) && (templine[i] != 0)) i++; retval = ActualFileWrite(outfile, i, templine); if(retval < i) { ActualFileClose(infile); infile = ACTUALHANDLENULL; ActualFileClose(outfile); outfile = ACTUALHANDLENULL; ActualFileDelete(outname); return(-1); } // ENDIF- Trouble writing it out? Abort. } // ENDIF- Are there other sections after this one? Save them then. } else { filepos = retval; ActualFileSeek(infile, filepos); filepos += INIReadLine(infile, templine); // Get section line's byte count retval = INIFindKeyword(infile, keyword, NULL); if(retval == -1) { ActualFileClose(infile); infile = ACTUALHANDLENULL; return(-1); } // ENDIF- Couldn't find the keyword? Abort filepos += retval; #ifdef VERBOSE_FUNCTION_INI PrintLog("CDVDiso ini: removing keyword"); #endif /* VERBOSE_FUNCTION_INI */ outfile = ActualFileOpenForWrite(outname); if(outfile == ACTUALHANDLENULL) { ActualFileClose(infile); infile = ACTUALHANDLENULL; return(-1); } // ENDIF- Couldn't open a temp file? Abort ActualFileSeek(infile, 0); retval = INICopy(infile, outfile, filepos); if(retval > 0) { ActualFileClose(infile); infile = ACTUALHANDLENULL; ActualFileClose(outfile); outfile = ACTUALHANDLENULL; ActualFileDelete(outname); return(-1); } // ENDIF- Trouble writing everything up to keyword? Abort. INIReadLine(infile, templine); // Read (and discard) the keyword line } // ENDIF- Wipe out the whole section? Or just a keyword? INICopy(infile, outfile, 0xFFFFFFF); // Write out rest of file ActualFileClose(infile); infile = ACTUALHANDLENULL; ActualFileClose(outfile); outfile = ACTUALHANDLENULL; ActualFileDelete(inname); ActualFileRename(outname, inname); return(0); } // END INIRemove() int INISaveUInt(char *file, char *section, char *keyword, unsigned int value) { char numvalue[INIMAXLEN+1]; sprintf(numvalue, "%u", value); return(INISaveString(file, section, keyword, numvalue)); } // END INISaveUInt() int INILoadUInt(char *file, char *section, char *keyword, unsigned int *buffer) { char numvalue[INIMAXLEN+1]; int retval; unsigned int value; // unsigned int sign; // Not needed in unsigned numbers int pos; if(buffer == NULL) return(-1); *(buffer) = 0; retval = INILoadString(file, section, keyword, numvalue); if(retval < 0) return(retval); value = 0; // sign = 1; // Start positive pos = 0; // Note: skip leading spaces? (Shouldn't have to, I hope) // if(numvalue[pos] == '-') { // pos++; // sign = -1; // } // ENDIF- Negative sign check while((pos < INIMAXLEN) && (numvalue[pos] != 0)) { if(value > (0xFFFFFFFF / 10)) return(-1); // Overflow? if((numvalue[pos] >= '0') && (numvalue[pos] <= '9')) { value *= 10; value += numvalue[pos] - '0'; pos++; } else { numvalue[pos] = 0; } // ENDIF- Add a digit in? Or stop searching for digits? } // ENDWHILE- Adding digits of info to our ever-increasing value // value *= sign *(buffer) = value; return(0); } // END INILoadUInt()