Subversion Repositories AndroidProjects

Rev

Blame | Last modification | View Log | RSS feed

#include "ModVal.h"
#include "Common.h"
#include <fstream>

struct ModStorage
{              
        bool                                    mChanged;
        int                                             mInt;
        double                                  mDouble;       
        std::string                             mString;
};

struct ModPointer
{
        const char *mStrPtr;
        int mLineNum;

        ModPointer() : mStrPtr(NULL), mLineNum(0) {}
        ModPointer(const char *theStrPtr, int theLineNum) : mStrPtr(theStrPtr), mLineNum(theLineNum) {}
};

typedef std::map<int,ModPointer> ModStorageMap; // stores counters

struct FileMod
{
        bool mHasMods;
        ModStorageMap mMap;

        FileMod(bool hasMods = false) { mHasMods = hasMods; }
};

typedef std::map<std::string, int> StringToIntMap;
typedef std::map<std::string, FileMod> FileModMap;

static StringToIntMap gStringToIntMap;
time_t gLastFileTime = 0;
const char *gSampleString = NULL; // for finding the others

static FileModMap& GetFileModMap()
{
        static FileModMap aMap;
        return aMap;
}

static const char* FindFileInStringTable(const std::string &theSearch, const char *theMem, DWORD theLength, const char *theStartPos)
{
        const char *aFind = NULL;
        try
        {
                aFind = std::search(theStartPos,theMem+theLength,theSearch.c_str(),theSearch.c_str()+theSearch.length());
                if (aFind>=theMem+theLength)
                        return NULL;
                else
                        return aFind;
        }
        catch(...)
        {
                return NULL;
        }

        return NULL;
}

static bool ParseModValString(std::string &theStr, int *theCounter = NULL, int *theLineNum = NULL)
{
        int aPos = theStr.length()-1;
        bool foundComma = false;
        while (aPos>0)
        {
                if (!foundComma && theStr[aPos]==',')
                {
                        aPos--;
                        foundComma = true;
                }
                else if (isdigit(theStr[aPos]))
                        aPos--;
                else
                        break;
        }

        if (aPos==theStr.length()-1 || aPos==0) // no number,number to erase... or empty file
                return false;

        aPos++;
        int aCounterVal = -1, aLineNum = -1;
        if (sscanf(theStr.c_str()+aPos,"%d,%d",&aCounterVal,&aLineNum)!=2) // couldn't parse out the numbers
                return false;

        theStr.resize(aPos);
        if (theCounter) *theCounter = aCounterVal;
        if (theLineNum) *theLineNum = aLineNum;
        return true;
}

static bool FindModValsInMemoryHelper(const char *theMem, DWORD theLength)
{
        std::string aSearchStr = "SEXYMODVAL";

        FileModMap &aMap = GetFileModMap();

        bool foundOne = false;
        const char *aPtr = theMem;
        while (true)
        {
                aPtr = FindFileInStringTable(aSearchStr,theMem,theLength,aPtr);
                if (aPtr==NULL)
                        break;

                int aCounter, aLineNum;
                std::string aFileName = aPtr+10; // skip SEXYMODVAL
                if (ParseModValString(aFileName,&aCounter,&aLineNum))
                {
                        if (aLineNum==4105)
                                _asm nop;

                        FileMod &aFileMod = aMap[aFileName];
                        aFileMod.mMap[aCounter] = ModPointer(aPtr-5,aLineNum);
                        foundOne = true;
                }
                aPtr++;
        }

        return foundOne;
}

static void FindModValsInMemory()
{
        MEMORY_BASIC_INFORMATION mbi;
        PVOID      pvAddress = 0;

        const char *aMem = NULL;
        DWORD aMemLength = 0;

        int aFound = 0;
        int aTotal = 0;
        memset(&mbi, 0, sizeof(MEMORY_BASIC_INFORMATION));
        for (; VirtualQuery(pvAddress, &mbi, sizeof(MEMORY_BASIC_INFORMATION)) == sizeof(MEMORY_BASIC_INFORMATION); pvAddress = ((BYTE*)mbi.BaseAddress) + mbi.RegionSize)
        {      
                const char *anAddress = (const char*)mbi.BaseAddress;
                if (mbi.State==MEM_COMMIT && mbi.Type==MEM_IMAGE)
                {
                        aTotal++;
                        if (aMem!=NULL && aMem+aMemLength==anAddress) // compact these two
                        {
                                aMemLength += mbi.RegionSize;
                                continue;
                        }
                       
                        if (aMem!=NULL) // do find in old section
                        {
                                if (FindModValsInMemoryHelper(aMem,aMemLength))
                                {
                                        aFound++;
                                        return;
                                }

                                aMem = NULL;
                        }

                        aMem = anAddress;
                        aMemLength = mbi.RegionSize;
                }
        }

        if (aMem!=NULL)
        {
                if (FindModValsInMemoryHelper(aMem,aMemLength))
                        aFound++;
        }
}

static ModStorage* CreateFileModsHelper(const char* theFileName)
{
        ModStorage *aModStorage = new ModStorage;
        aModStorage->mChanged = false;

        // Change this thinggie
        DWORD anOldProtect;
        VirtualProtect((LPVOID) theFileName, 5, PAGE_READWRITE, &anOldProtect);
        *((char*) theFileName) = 0;
        *((ModStorage**) (theFileName+1)) = aModStorage;
        VirtualProtect((LPVOID) theFileName, 5, anOldProtect, &anOldProtect);
       
        return aModStorage;    
}


static ModStorage* CreateFileMods(const char* theFileName)
{      
        if (gSampleString==NULL)
                gSampleString = theFileName;

        std::string aFileName = theFileName+15; // skip SEXY_SEXYMODVAL
        ParseModValString(aFileName);

        FileModMap &aMap = GetFileModMap();
        aMap[aFileName].mHasMods = true;

        return CreateFileModsHelper(theFileName);
}

int Sexy::ModVal(int theAreaNum, const char* theFileName, int theInt)
{      
        if (*theFileName != 0)
                CreateFileMods(theFileName);   

        ModStorage* aModStorage = *(ModStorage**)(theFileName+1);
        if (aModStorage->mChanged)
                return aModStorage->mInt;
        else
                return theInt;
}

double Sexy::ModVal(int theAreaNum, const char* theFileName, double theDouble)
{
        if (*theFileName != 0)
                CreateFileMods(theFileName);   
                       
        ModStorage* aModStorage = *(ModStorage**)(theFileName+1);
        if (aModStorage->mChanged)
                return aModStorage->mDouble;
        else
                return theDouble;
}

float Sexy::ModVal(int theAreaNum, const char* theFileName, float theFloat)
{
        return (float) ModVal(theAreaNum, theFileName, (double) theFloat);
}

const char*     Sexy::ModVal(int theAreaNum, const char* theFileName, const char *theStr)
{
        if (*theFileName != 0)
                CreateFileMods(theFileName);   

        ModStorage* aModStorage = *(ModStorage**)(theFileName+1);
        if (aModStorage->mChanged)
                return aModStorage->mString.c_str();
        else
                return theStr;
}


void Sexy::AddModValEnum(const std::string &theEnumName, int theVal)
{
        gStringToIntMap[theEnumName] = theVal;
}

static bool ModStringToInteger(const char* theString, int* theIntVal)
{
        *theIntVal = 0;

        int theRadix = 10;
        bool isNeg = false;

        unsigned i = 0;

        if (isalpha((unsigned char)theString[i]) || theString[i]=='_') // enum
        {
               
                std::string aStr;
                while (isalnum((unsigned char)theString[i]) || theString[i]=='_')
                {
                        aStr += theString[i];
                        i++;
                }

                StringToIntMap::iterator anItr = gStringToIntMap.find(aStr);
                if (anItr!=gStringToIntMap.end())
                {
                        *theIntVal = anItr->second;
                        return true;
                }

                i = 0;
        }
               
        if (theString[i] == '-')
        {
                isNeg = true;
                i++;
        }

        for (;;)
        {
                char aChar = theString[i];
               
                if ((theRadix == 10) && (aChar >= '0') && (aChar <= '9'))
                        *theIntVal = (*theIntVal * 10) + (aChar - '0');
                else if ((theRadix == 0x10) &&
                        (((aChar >= '0') && (aChar <= '9')) ||
                         ((aChar >= 'A') && (aChar <= 'F')) ||
                         ((aChar >= 'a') && (aChar <= 'f'))))
                {                      
                        if (aChar <= '9')
                                *theIntVal = (*theIntVal * 0x10) + (aChar - '0');
                        else if (aChar <= 'F')
                                *theIntVal = (*theIntVal * 0x10) + (aChar - 'A') + 0x0A;
                        else
                                *theIntVal = (*theIntVal * 0x10) + (aChar - 'a') + 0x0A;
                }
                else if (((aChar == 'x') || (aChar == 'X')) && (i == 1) && (*theIntVal == 0))
                {
                        theRadix = 0x10;
                }
                else if (aChar == ')')
                {
                        if (isNeg)
                                *theIntVal = -*theIntVal;
                        return true;
                }
                else
                {
                        *theIntVal = 0;
                        return false;
                }

                i++;
        }              
}

static bool ModStringToDouble(const char* theString, double* theDoubleVal)
{
        *theDoubleVal = 0.0;

        bool isNeg = false;

        unsigned i = 0;
        if (theString[i] == '-')
        {
                isNeg = true;
                i++;
        }

        for (;;)
        {
                char aChar = theString[i];

                if ((aChar >= '0') && (aChar <= '9'))
                        *theDoubleVal = (*theDoubleVal * 10) + (aChar - '0');
                else if (aChar == '.')
                {
                        i++;
                        break;
                }              
                else if ((aChar == ')') || ((aChar == 'f') && (theString[i+1] == ')'))) // At end
                {
                        if (isNeg)
                                *theDoubleVal = -*theDoubleVal;
                        return true;
                }
                else
                {
                        *theDoubleVal = 0.0;
                        return false;
                }

                i++;
        }

        double aMult = 0.1;
        for (;;)
        {
                char aChar = theString[i];

                if ((aChar >= '0') && (aChar <= '9'))
                {
                        *theDoubleVal += (aChar - '0') * aMult;
                        aMult /= 10.0;
                }
                else if ((aChar == ')') || ((aChar == 'f') && (theString[i+1] == ')'))) // At end
                {
                        if (isNeg)
                                *theDoubleVal = -*theDoubleVal;
                        return true;
                }
                else
                {
                        *theDoubleVal = 0.0;
                        return false;
                }

                i++;
        }
}

static bool ModStringToString(const char* theString, std::string &theStrVal)
{
        if (theString[0]!='"')
                return false;

        std::string &aStr = theStrVal;
        aStr.erase();

        int i=1;
        while (true)
        {
                if (theString[i]=='\\')
                {
                        i++;
                        switch (theString[i++])
                        {
                        case 'n': aStr += '\n'; break;
                        case 't': aStr += '\t'; break;
                        case '\\': aStr += '\\'; break;
                        case '"': aStr += '\"'; break;
                        default: return false;
                        }
                }
                else if (theString[i]=='"')
                {
                        i++;
                        while (isspace((unsigned char)theString[i]))
                                i++;

                        if (theString[i]!='"') // continued string
                                return true;
                        else
                                break;
                }
                else if (theString[i]=='\0')
                        return false;
                else
                        aStr += theString[i++];
        }

        return true;
}

bool Sexy::ReparseModValues()
{
        if (gLastFileTime == 0)
        {
                char anEXEName[256];
                GetModuleFileNameA(NULL, anEXEName, 256);
                gLastFileTime = GetFileDate(anEXEName);
               
                FindModValsInMemory();
        }

        bool hasNewFiles = false;
        std::string aFileList;

        // Process each file one at a time
        FileModMap &aMap = GetFileModMap();
        FileModMap::iterator aFileModItr;
        for (aFileModItr = aMap.begin(); aFileModItr != aMap.end(); ++aFileModItr)
        {
                FileMod &aFileMod = aFileModItr->second;
                if (!aFileMod.mHasMods)
                        continue;

                ModStorageMap &aModMap = aFileMod.mMap;
                std::string aFileName = aFileModItr->first;

                time_t aThisTime = GetFileDate(aFileName);
                if (aThisTime > gLastFileTime)
                {
                        gLastFileTime = aThisTime;
                        hasNewFiles = true;
                }

                if (aFileList.length() > 0)
                        aFileList += "\r\n  ";
                aFileList += aFileName;

                int aLineNum = 1;
                int aModNum = 0;
                ModStorageMap::iterator aModMapItr = aModMap.begin();

                std::fstream aStream(aFileName.c_str(), std::ios::in);

                if (aStream.is_open())
                {
                        while (!aStream.eof())
                        {
                                char aString[8192];
                                aStream.getline(aString, 8192);

                                int aCharIdx = 0;
                                int aChar = 0;
                                int aLastChar = 0;
                                while (aString[aCharIdx] != 0)
                                {
                                        aLastChar = aChar;
                                        aChar = aString[aCharIdx];
                                       
                                        if (aChar == '"')  // Skip strings
                                        {
                                                while (true)
                                                {
                                                        aLastChar = aChar;
                                                        aChar = aString[++aCharIdx];

                                                        if (aChar=='\\' && aLastChar=='\\') // so we don't interpret \\" as an escaped quote
                                                                aChar = 0;
                                                        else if (aChar=='"' && aLastChar!='\\')
                                                                break;
                                                        else if (aChar==0)
                                                        {
                                                                if (aLastChar=='\\') // continuation
                                                                {
                                                                        aCharIdx = -1;
                                                                        aChar = 0;
                                                                        aLastChar = 0;
                                                                        aLineNum++;

                                                                        aStream.getline(aString, 8192);
                                                                        if (aString[0]!=0 || !aStream.eof()) // got valid new line
                                                                                continue;
                                                                }

                                                                char aStr[512];
                                                                sprintf(aStr, "ERROR in %s on line %d: Error parsing quotes", aFileName.c_str(), aLineNum);
                                                                MessageBoxA(NULL, aStr, "MODVAL ERROR", MB_OK | MB_ICONERROR);
                                                                return false;
                                                        }
                                                }
                                        }
                                        else if (aChar=='/') // Skip C++ comments
                                        {
                                                if (aLastChar=='/')
                                                {

                                                        while (true)
                                                        {
                                                                aLastChar = aChar;
                                                                aChar = aString[++aCharIdx];
                                                                if (aChar==0) // line continuation
                                                                {
                                                                        if (aLastChar=='\\') // continuation
                                                                        {
                                                                                aCharIdx = -1;
                                                                                aChar = 0;
                                                                                aLastChar = 0;
                                                                                aLineNum++;

                                                                                aStream.getline(aString, 8192);
                                                                                if (aString[0]!=0 || !aStream.eof()) // got valid new line
                                                                                        continue;
                                                                        }
                                                                        else
                                                                        {
                                                                                aCharIdx--;
                                                                                break;
                                                                        }

                                                                        char aStr[512];
                                                                        sprintf(aStr, "ERROR in %s on line %d: Error parsing c++ comment", aFileName.c_str(), aLineNum);
                                                                        MessageBoxA(NULL, aStr, "MODVAL ERROR", MB_OK | MB_ICONERROR);
                                                                        return false;
                                                                }
                                                        }
                                                }
                                        }
                                        else if (aChar=='*') // skip C comments
                                        {
                                                if (aLastChar=='/')
                                                {
                                                        while (true)
                                                        {
                                                                aLastChar = aChar;
                                                                aChar = aString[++aCharIdx];
                                                                if (aChar=='/' && aLastChar=='*')
                                                                        break;
                                                                else if (aChar==0) // line continuation
                                                                {
                                                                        aCharIdx = -1;
                                                                        aChar = 0;
                                                                        aLastChar = 0;
                                                                        aLineNum++;

                                                                        aStream.getline(aString, 8192);
                                                                        if (aString[0]!=0 || !aStream.eof()) // got valid new line
                                                                                continue;

                                                                        char aStr[512];
                                                                        sprintf(aStr, "ERROR in %s on line %d: Error parsing c comment", aFileName.c_str(), aLineNum);
                                                                        MessageBoxA(NULL, aStr, "MODVAL ERROR", MB_OK | MB_ICONERROR);
                                                                        return false;
                                                                }
                                                        }
                                                }
                                        }
                                        else if (aChar == '(')
                                        {
                                                int theAreaNum = -1;
                                                if ((aCharIdx >= 2) && (aString[aCharIdx-1] == 'M') &&
                                                        (!isalpha((unsigned char) aString[aCharIdx-2])))
                                                {
                                                        theAreaNum = 0;                                                
                                                }
                                                else if ((aCharIdx >= 3) &&
                                                        (aString[aCharIdx-1] >= '1') && (aString[aCharIdx-1] <= '9') &&
                                                        (aString[aCharIdx-2] == 'M') &&
                                                        (!isalpha((unsigned char) aString[aCharIdx-3])))
                                                {
                                                        theAreaNum = aString[aCharIdx-1] - '0';
                                                }

                                                if (theAreaNum != -1)
                                                {
                                                        while (aModMapItr!=aModMap.end() && aModMapItr->second.mLineNum<aLineNum)
                                                                ++aModMapItr;

                                                        if (aModMapItr!=aModMap.end() && aModMapItr->second.mLineNum==aLineNum)
                                                        {
                                                                const char *aPtr = aModMapItr->second.mStrPtr;
                                                                aModMapItr++;

                                                                int anIntVal = 0;
                                                                double aDoubleVal = 0.0;
                                                                std::string aStrVal;

                                                                // Try to parse out a number
                                                                if ((ModStringToString(aString + aCharIdx + 1, aStrVal)) ||
                                                                        (ModStringToInteger(aString + aCharIdx + 1, &anIntVal)) ||
                                                                        (ModStringToDouble(aString + aCharIdx + 1, &aDoubleVal)))
                                                                {                                              
                                                                        // We found a mod value!

                                                                        if (*aPtr!=0) // have stored something here
                                                                                CreateFileMods(aPtr);

                                                                        ModStorage* aModStorage = *(ModStorage**)(aPtr+1);
                                                                        aModStorage->mInt = anIntVal;
                                                                        aModStorage->mDouble = aDoubleVal;
                                                                        aModStorage->mString = aStrVal;
                                                                        aModStorage->mChanged = true;
                                                                }
                                                                else
                                                                {
                                                                        char aStr[512];
                                                                        sprintf(aStr, "ERROR in %s on line %d.  Parsing Error.", aFileName.c_str(), aLineNum);
                                                                        MessageBoxA(NULL, aStr, "MODVAL ERROR", MB_OK | MB_ICONERROR);

                                                                        return false;
                                                                }
                                                        }
                                                        else
                                                        {
                                                                // Functions can be optimized out
                                                        }
                                                }
                                        }

                                        aCharIdx++;
                                }

                                aLineNum++;
                        }
                }
                else
                {
                        MessageBoxA(NULL, (std::string("ERROR: Unable to open ") + aFileName + " for reparsing.").c_str(), "MODVAL ERROR!", MB_OK | MB_ICONERROR);
                        return false;
                }              
        }

        if (!hasNewFiles)
        {
                if (aFileList.length() == 0)
                        aFileList = "none";
                MessageBoxA(NULL, (std::string("WARNING: No file changes detected.  Files parsed: \r\n  ") + aFileList).c_str(), "MODVAL WARNING!", MB_OK | MB_ICONWARNING);
                return false;
        }

        return true;
}