/////////////////////////////////////////////////////////////////////////////////////// // // string.cpp KSH 17 Oct 2001 // // Code for managing allocation and deallocation of strings within the script system. // For example it manages the 'permanent string' heap, which is for strings that get created // when all the qb's are loaded and never get deleted. They can therefore all be squashed // together for optimum use of memory. // // Note: Not defining a CString class, because it would have to have a char pointer so // would just be an unneccessary layer of indirection that would waste space. // // Instead CreateString and DeleteString are used to allocate and deallocate strings. // // Could add cleverer memory management here later, eg a pool of fixed sized strings // for strings that need to be alloc'd and dealloc'd fast. // /////////////////////////////////////////////////////////////////////////////////////// // Note: Small memory saving possible by deleting the sp_permanent_string_checksums array // once you know that no more permanent strings will need to be allocated. Will save 32K ish, // depending on maxStrings. // (There's no function here yet for doing that, but easy to add) #include #ifndef __CORE_CRC_H #include #endif namespace Script { static char *sAddToSpecialStringHeap(const char *p_string); static bool s_use_permanent_string_heap=false; // The permanent string buffer. static char *sp_permanent_string_heap=NULL; static char *sp_permanent_string_heap_top=NULL; static uint32 s_permanent_string_heap_size=0; // Array of string checksums to allow quick checking to see if a particular string // is already in the permanent string buffer without having to do string comparisons. // Needed because when loading several thousand strings, several million string comparisons // would have to be done, causing a significant pause in loading the game. struct SSpecialStringChecksum { uint32 mChecksum; char *mpString; }; static SSpecialStringChecksum *sp_permanent_string_checksums=NULL; static uint32 s_num_permanent_string_checksums=0; static uint32 s_max_permanent_string_checksums=0; // Needs to be called once at the start of the game. // For THPS3, maxSize was 48000 for English, 60500 for other languages. maxStrings was 4000 void AllocatePermanentStringHeap(uint32 maxSize, uint32 maxStrings) { // Uses whatever the current heap is set to, which is set to the script heap in Script::AllocatePools // which is where this function is called from. s_permanent_string_heap_size=maxSize; s_max_permanent_string_checksums=maxStrings; // Allocate the string buffer. Dbg_MsgAssert(sp_permanent_string_heap==NULL,("sp_permanent_string_heap not NULL ???")); sp_permanent_string_heap=(char*)Mem::Malloc(s_permanent_string_heap_size); sp_permanent_string_heap_top=sp_permanent_string_heap; // Allocate the array of checksums. Dbg_MsgAssert(sp_permanent_string_checksums==NULL,("sp_permanent_string_checksums not NULL ???")); sp_permanent_string_checksums=(SSpecialStringChecksum*)Mem::Malloc(s_max_permanent_string_checksums*sizeof(SSpecialStringChecksum)); s_num_permanent_string_checksums=0; } void DeallocatePermanentStringHeap() { // Deallocate the string buffer. Dbg_MsgAssert(sp_permanent_string_heap,("NULL sp_permanent_string_heap ?")); Mem::Free(sp_permanent_string_heap); sp_permanent_string_heap=NULL; sp_permanent_string_heap_top=NULL; s_permanent_string_heap_size=0; // Deallocate the array of checksums. // MEMOPT: TODO: Destroy this when UseRegularStringHeap is called too, to free up memory ? Dbg_MsgAssert(sp_permanent_string_checksums,("NULL sp_permanent_string_checksums ?")); Mem::Free(sp_permanent_string_checksums); sp_permanent_string_checksums=NULL; s_num_permanent_string_checksums=0; s_max_permanent_string_checksums=0; } // Adds the contents of the passed pString to the special string heap, and returns // the pointer to the string within the heap. // If the string is already found in the heap, it won't add it again. static char *sAddToSpecialStringHeap(const char *p_string) { Dbg_MsgAssert(p_string,("NULL p_string")); // Check to see if it already exists in the special string heap ... // Calculate the checksum of the string, then search for this in the array of // checksums of all the special strings so far. // Quicker than doing byte comparisons, probably due to the long time it takes to // read single bytes from memory. // Needs to be fast, otherwise it can add a significant delay to the startup time. // This method adds about 0.3 seconds altogether (in THPS3, couple of thousand strings) uint32 len=strlen(p_string); uint32 checksum=Crc::GenerateCRCCaseSensitive(p_string,len); Dbg_MsgAssert(sp_permanent_string_checksums,("NULL sp_permanent_string_checksums ??")); SSpecialStringChecksum *p_ch=sp_permanent_string_checksums; for (uint32 i=0; imChecksum==checksum) { return p_ch->mpString; } ++p_ch; } char *p_heap_string=NULL; // Not found, so add to the pile of strings. Dbg_MsgAssert(sp_permanent_string_heap,("NULL sp_permanent_string_heap ??")); Dbg_MsgAssert(s_permanent_string_heap_size-(sp_permanent_string_heap_top-sp_permanent_string_heap)>=len+1,("Out of special string heap")); p_heap_string=sp_permanent_string_heap_top; strcpy(p_heap_string,p_string); // Update sp_permanent_string_heap_top sp_permanent_string_heap_top+=len; ++sp_permanent_string_heap_top; // Skip over the terminator // Store the checksum too. Dbg_MsgAssert(s_num_permanent_string_checksums=sp_permanent_string_heap && p_string