From 67d5aeadb0fabb82416940c5d0026f573f01dd0f Mon Sep 17 00:00:00 2001 From: Sevenfm Date: Wed, 13 Apr 2022 10:43:49 +0000 Subject: [PATCH] Additional Laptop files (by sun_alf). Example file: https://github.com/sun-alf/BRAINMOD-o/blob/master/Data-BRAINMOD/TableData/Laptop/AdditionalFiles.xml git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@9363 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- Laptop/files.cpp | 461 +++++++++++++++---- Laptop/files.h | 17 +- Standard Gaming Platform/FileMan.cpp | 35 ++ Standard Gaming Platform/FileMan.h | 2 + Standard Gaming Platform/Font.cpp | 50 +- Standard Gaming Platform/Font.h | 3 + Tactical/XML.h | 2 + Utils/WordWrap.cpp | Bin 116834 -> 116688 bytes Utils/WordWrap.h | 28 +- ext/VFS/include/vfs/Tools/vfs_parser_tools.h | 6 +- ext/VFS/src/Tools/vfs_parser_tools.cpp | 42 +- 11 files changed, 503 insertions(+), 143 deletions(-) diff --git a/Laptop/files.cpp b/Laptop/files.cpp index 4fccebda..08fd9637 100644 --- a/Laptop/files.cpp +++ b/Laptop/files.cpp @@ -3,6 +3,7 @@ #else #include "builddefines.h" #include + #include #include "laptop.h" #include "files.h" #include "Game clock.h" @@ -17,6 +18,9 @@ // HEADROCK PROFEX: This is required to display the proper facial image. #include "Soldier Profile.h" #include "GameSettings.h" + #include "XML.h" + #include "expat.h" + #include "Debug Control.h" #endif #ifdef JA2UB @@ -54,6 +58,7 @@ #define LENGTH_OF_ENRICO_FILE 68 #endif #define MAX_FILE_MESSAGE_PAGE_SIZE 325 +#define MAX_TEXT_FILE_MESSAGE_PAGE_SIZE (350) #define VIEWER_MESSAGE_BODY_START_Y FILES_LIST_Y #define PREVIOUS_FILE_PAGE_BUTTON_X iScreenWidthOffset + 553 #define PREVIOUS_FILE_PAGE_BUTTON_Y iScreenHeightOffset + 53 @@ -205,6 +210,247 @@ FileRecordWidthPtr CreateWidthRecordsForAruloIntelFile( void ); FileRecordWidthPtr CreateWidthRecordsForTerroristFile( void ); FileRecordWidthPtr CreateRecordWidth( INT32 iRecordNumber, INT32 iRecordWidth, INT32 iRecordHeightAdjustment, UINT8 ubFlags ); +// sun_alf: functionality of additional files processing +#define ADDFILES_NAME_MAX_LENGTH (16) +#define ADDFILES_PATH_MAX_LENGTH (128) +typedef struct +{ + CHAR16 name[ADDFILES_NAME_MAX_LENGTH + 1]; // in-game displayed name of file + CHAR8 path[ADDFILES_PATH_MAX_LENGTH + 1]; // local path to file under "/Laptop" dir + BOOLEAN atInit; // is the file available from game init + UINT8 font; // font code + UINT8 textColor; // text color code + UINT8 bkgdColor; // text background color code + UINT8 fileCode; // file code to integrate with vanilla files (starts with ADDITIONAL_FILE_0) +} AdditionalFiles_Descriptor; + +typedef enum +{ + ADDFILES_ELEMENT_NONE = 0, + ADDFILES_ELEMENT_ADDITIONALFILES, + ADDFILES_ELEMENT_FILE, + ADDFILES_ELEMENT, +} AdditionalFiles_ParseStage; + +typedef struct +{ + AdditionalFiles_ParseStage curElement; + CHAR8 szCharData[MAX_CHAR_DATA_LENGTH + 1]; + UINT32 currentDepth; + UINT32 maxReadDepth; + UINT8 fileCode; + + AdditionalFiles_Descriptor parsedData; +} AdditionalFiles_ParseData; + +static BOOLEAN HandleAdditionalTextFile( FilesUnitPtr file ); + +static std::list g_AdditionalFilesList; + + +static AdditionalFiles_Descriptor* AdditionalFiles_GetDescriptor( UINT8 ubCode ) +{ + for ( AdditionalFiles_Descriptor &item : g_AdditionalFilesList ) + { + if ( item.fileCode == ubCode ) + { + return &item; + } + } + return NULL; +} + +static AdditionalFiles_Descriptor* AdditionalFiles_LoadTextFile( FilesUnitPtr file ) +{ + AdditionalFiles_Descriptor *descr = AdditionalFiles_GetDescriptor( file->ubCode ); + + if ( descr ) + { + ClearFileStringList(); + + CHAR8 fileName[MAX_PATH + 1] = TABLEDATA_DIRECTORY TABLEDATA_LAPTOP_DIRECTORY; + strncat( fileName, descr->path, MAX_PATH ); + if ( FileExists( fileName ) ) + { + HWFILE hFile = FileOpen( fileName, FILE_ACCESS_READ, FALSE ); + if ( hFile ) + { + std::string utf8Line; + while ( FileReadLine( hFile, &utf8Line ) ) + { + std::wstring wcharLine = utf8_to_wstring( utf8Line ); + AddStringToFilesList( (STR16) wcharLine.c_str() ); + } + FileClose( hFile ); + } + } + } + + return descr; +} + +static INT32 AdditionalFiles_GetFontHandler( UINT8 font ) +{ + INT32 iFont = (INT32)font; + if ( IsFontLoaded( iFont ) == FALSE ) + iFont = FILES_TEXT_FONT; + + return iFont; +} + +static BOOLEAN AdditionalFiles_IsValid( AdditionalFiles_Descriptor *descr ) +{ + BOOLEAN result = FALSE; + CHAR8 fileName[MAX_PATH] = TABLEDATA_DIRECTORY TABLEDATA_LAPTOP_DIRECTORY; + strcat( fileName, descr->path ); + BOOLEAN exists = FileExists( fileName ); + + if ( exists && wcslen( descr->name ) > 0 && + (INT32)descr->font <= MAX_FONTS && + ADDITIONAL_FILE_0 <= descr->fileCode && descr->fileCode <= ADDITIONAL_FILE_MAX ) + { + result = TRUE; + } + + return result; +} + +// Process the opening tag in this expat callback. +static void XMLCALL AdditionalFiles_StartElementHandler( void *userData, const XML_Char *name, const XML_Char **atts ) +{ + AdditionalFiles_ParseData *pData = (AdditionalFiles_ParseData*) userData; + + if ( pData->currentDepth <= pData->maxReadDepth ) + { + if ( strcmp( name, "AdditionalFiles" ) == 0 && pData->curElement == ADDFILES_ELEMENT_NONE ) + { + pData->fileCode = ADDITIONAL_FILE_0; // assign ID to each entry + pData->curElement = ADDFILES_ELEMENT_ADDITIONALFILES; + pData->maxReadDepth++; + } + else if ( strcmp( name, "File" ) == 0 && pData->curElement == ADDFILES_ELEMENT_ADDITIONALFILES ) + { + memset( &pData->parsedData, 0, sizeof(pData->parsedData) ); + pData->curElement = ADDFILES_ELEMENT_FILE; + pData->maxReadDepth++; + } + else if ( pData->curElement == ADDFILES_ELEMENT_FILE && + (strcmp( name, "Name" ) == 0 || + strcmp( name, "Path" ) == 0 || + strcmp( name, "AtInit" ) == 0 || + strcmp( name, "Font" ) == 0 || + strcmp( name, "TextColor" ) == 0 || + strcmp( name, "BkgdColor" ) == 0) ) + { + pData->curElement = ADDFILES_ELEMENT; + pData->maxReadDepth++; + } + pData->szCharData[0] = '\0'; + } + pData->currentDepth++; +} + +// Process any text content in this callback. +static void XMLCALL AdditionalFiles_CharacterDataHandler( void *userData, const XML_Char *str, int len ) +{ + AdditionalFiles_ParseData *pData = (AdditionalFiles_ParseData*) userData; + + if ( pData->currentDepth <= pData->maxReadDepth && strlen( pData->szCharData ) < MAX_CHAR_DATA_LENGTH ) + strncat( pData->szCharData, str, __min( (unsigned int)len, MAX_CHAR_DATA_LENGTH - strlen( pData->szCharData ) ) ); +} + +// Process the closing tag in this expat callback. +static void XMLCALL AdditionalFiles_EndElementHandler( void *userData, const XML_Char *name ) +{ + AdditionalFiles_ParseData *pData = (AdditionalFiles_ParseData*) userData; + + if ( pData->currentDepth <= pData->maxReadDepth ) + { + if ( pData->curElement == ADDFILES_ELEMENT_ADDITIONALFILES && strcmp( name, "AdditionalFiles" ) == 0 ) + { + pData->curElement = ADDFILES_ELEMENT_NONE; + } + else if ( pData->curElement == ADDFILES_ELEMENT_FILE && strcmp( name, "File" ) == 0 ) + { + pData->parsedData.fileCode = pData->fileCode++; + if ( AdditionalFiles_IsValid( &pData->parsedData ) == TRUE ) + { + g_AdditionalFilesList.push_back( pData->parsedData ); + } + + pData->curElement = ADDFILES_ELEMENT_ADDITIONALFILES; + } + else if ( pData->curElement == ADDFILES_ELEMENT ) + { + if ( strcmp( name, "Name" ) == 0 ) + { + std::string strName( pData->szCharData ); + std::wstring wstrName = utf8_to_wstring( strName ); + wcsncpy( pData->parsedData.name, wstrName.c_str(), ADDFILES_NAME_MAX_LENGTH ); + } + else if ( strcmp( name, "Path" ) == 0 ) + strncpy( (CHAR8*)pData->parsedData.path, pData->szCharData, ADDFILES_PATH_MAX_LENGTH ); + else if ( strcmp( name, "AtInit" ) == 0 ) + pData->parsedData.atInit = (BOOLEAN)atol( pData->szCharData ); + else if ( strcmp( name, "Font" ) == 0 ) + pData->parsedData.font = (UINT8)atol( pData->szCharData ); + else if ( strcmp( name, "TextColor" ) == 0 ) + pData->parsedData.textColor = (UINT8)atol( pData->szCharData ); + else if ( strcmp( name, "BkgdColor" ) == 0 ) + pData->parsedData.bkgdColor = (UINT8)atol( pData->szCharData ); + + pData->curElement = ADDFILES_ELEMENT_FILE; + } + + pData->maxReadDepth--; + } + pData->currentDepth--; +} + +// Parse AdditionalFiles.xml and build list of file descriptors +static void LocateAdditionalFiles( ) +{ + g_AdditionalFilesList.clear(); + + CHAR8 fileName[MAX_PATH] = TABLEDATA_DIRECTORY TABLEDATA_LAPTOP_DIRECTORY; + strcat( fileName, LAPTOPADDITIONALFILESFILENAME ); + + if ( FileExists( fileName ) ) + { + HWFILE hFile = FileOpen( fileName, FILE_ACCESS_READ, FALSE ); + if ( hFile ) + { + UINT32 uiBytesRead; + UINT32 uiFileSize = FileGetSize( hFile ); + CHAR8 *fileBuffer = (CHAR8*)MemAlloc( uiFileSize + 1 ); + + if ( FileRead( hFile, fileBuffer, uiFileSize, &uiBytesRead ) ) + { + XML_Parser parser = XML_ParserCreate( NULL ); + XML_SetElementHandler( parser, AdditionalFiles_StartElementHandler, AdditionalFiles_EndElementHandler ); + XML_SetCharacterDataHandler( parser, AdditionalFiles_CharacterDataHandler ); + + AdditionalFiles_ParseData pData; + memset( &pData, 0, sizeof( pData ) ); + XML_SetUserData( parser, &pData ); + + fileBuffer[uiFileSize] = 0; // put a safe-guard null terminator + if ( XML_Parse( parser, fileBuffer, uiFileSize, TRUE ) != XML_STATUS_OK ) + { + CHAR8 errorBuf[MAX_CHAR_DATA_LENGTH]; + sprintf( errorBuf, "XML Parser Error in %s: %s at line %d", LAPTOPADDITIONALFILESFILENAME, XML_ErrorString( XML_GetErrorCode( parser ) ), XML_GetCurrentLineNumber( parser ) ); + LiveMessage( errorBuf ); + } + + XML_ParserFree( parser ); + } + + MemFree( fileBuffer ); + FileClose( hFile ); + } + } +} + UINT32 AddFilesToPlayersLog(UINT8 ubCode, UINT32 uiDate, UINT8 ubFormat, STR8 pFirstPicFile, STR8 pSecondPicFile ) { @@ -229,10 +475,10 @@ UINT32 AddFilesToPlayersLog(UINT8 ubCode, UINT32 uiDate, UINT8 ubFormat, STR8 pF // return unique id of this transaction return uiId; } + void GameInitFiles( ) { - - if ( (FileExists( FILES_DAT_FILE ) == TRUE ) ) + if ( FileExists( FILES_DAT_FILE ) == TRUE ) { FileClearAttributes( FILES_DAT_FILE ); FileDelete( FILES_DAT_FILE ); @@ -241,14 +487,16 @@ void GameInitFiles( ) ClearFilesList( ); // add background check by RIS - - //#ifdef JA2UB - //if ( gGameUBOptions.RISRAPORT == TRUE ) - // AddFilesToPlayersLog( ENRICO_BACKGROUND, 0,255, NULL, NULL ); - //#else AddFilesToPlayersLog( ENRICO_BACKGROUND, 0,255, NULL, NULL ); - //#endif - + + // additional files: add all available from game beginning files + LocateAdditionalFiles(); + for ( AdditionalFiles_Descriptor item : g_AdditionalFilesList ) + { + if ( item.atInit ) + AddFilesToPlayersLog( item.fileCode, 0, FFORMAT_ADDITIONAL_TEXT, NULL, NULL ); + } + //mission briefing by Jazz //AddFilesToPlayersLog( MISSION_BRIEFING, 0,4, NULL, NULL ); } @@ -274,6 +522,10 @@ void EnterFiles() // now set start states HandleFileViewerButtonStates( ); + // Re-locate additional files: parse AdditionalFiles.xml and build descriptors. Processing from scratch on each Laptop + // files mode opening come handy for modders, also it follows such rule of this (files.cpp) module. + LocateAdditionalFiles( ); + // build files list OpenAndReadFilesFile( ); @@ -292,7 +544,6 @@ void EnterFiles() void ExitFiles() { - // write files list out to disk OpenAndWriteFilesFile( ); @@ -306,6 +557,9 @@ void ExitFiles() // remove files RemoveFiles( ); + + // clear and allow to re-load all the stuff at next Laptop opening + g_AdditionalFilesList.clear(); } void HandleFiles() @@ -703,13 +957,22 @@ void DisplayFilesList( void ) { if (iCounter==iHighLightFileLine) { - // render highlight - GetVideoObject(&hHandle, guiHIGHLIGHT); - BltVideoObject(FRAME_BUFFER, hHandle, 0, FILES_SENDER_TEXT_X - 5, iScreenHeightOffset + ( ( iCounter + 9 ) * BLOCK_HEIGHT) + ( iCounter * 2 ) - 4 , VO_BLT_SRCTRANSPARENCY,NULL); + // render highlight + GetVideoObject(&hHandle, guiHIGHLIGHT); + BltVideoObject(FRAME_BUFFER, hHandle, 0, FILES_SENDER_TEXT_X - 5, iScreenHeightOffset + ( ( iCounter + 9 ) * BLOCK_HEIGHT) + ( iCounter * 2 ) - 4 , VO_BLT_SRCTRANSPARENCY,NULL); + } + if ( pFilesList->ubCode <= LAST_JA2_VANILLA_FILE ) + { + mprintf( FILES_SENDER_TEXT_X, iScreenHeightOffset + ((iCounter + 9) * BLOCK_HEIGHT) + (iCounter * 2) - 2, pFilesSenderList[pFilesList->ubCode] ); + } + else // additional file case + { + AdditionalFiles_Descriptor *descr = AdditionalFiles_GetDescriptor( pFilesList->ubCode ); + if ( descr ) + mprintf( FILES_SENDER_TEXT_X, iScreenHeightOffset + ((iCounter + 9) * BLOCK_HEIGHT) + (iCounter * 2) - 2, descr->name ); } - mprintf(FILES_SENDER_TEXT_X, iScreenHeightOffset + (( iCounter + 9 ) * BLOCK_HEIGHT) + ( iCounter * 2 ) - 2 ,pFilesSenderList[pFilesList->ubCode]); iCounter++; pFilesList=pFilesList->Next; } @@ -836,11 +1099,10 @@ BOOLEAN DisplayFormattedText( void ) UINT16 usFirstHeight = 0; UINT16 usSecondWidth; UINT16 usSecondHeight; - INT32 iCounter=0; - INT32 iLength=0; - INT32 iHeight=0; - INT32 iOffSet=0; - INT32 iMessageCode; + UINT32 uiCounter = 0; + UINT32 uiLength = 0; + UINT32 uiHeight = 0; + UINT32 uiOffSet = 0; CHAR16 sString[2048]; HVOBJECT hHandle; UINT32 uiFirstTempPicture; @@ -852,16 +1114,11 @@ BOOLEAN DisplayFormattedText( void ) fWaitAFrame = FALSE; // get the file that was highlighted - while(iCounter < iHighLightFileLine) + for ( INT32 i = 0; i < iHighLightFileLine; i++ ) { - iCounter++; - pFilesList=pFilesList->Next; + pFilesList = pFilesList->Next; } - // message code found, reset counter - iMessageCode = pFilesList->ubCode; - iCounter=0; - // set file as read pFilesList->fRead = TRUE; @@ -873,49 +1130,33 @@ BOOLEAN DisplayFormattedText( void ) BltVideoObject(FRAME_BUFFER, hHandle, 0, FILE_VIEWER_X, FILE_VIEWER_Y - 4, VO_BLT_SRCTRANSPARENCY,NULL); // get the offset in the file - while( iCounter < iMessageCode) + if ( pFilesList->ubCode <= LAST_JA2_VANILLA_FILE ) { - // increment increment offset - iOffSet+=ubFileRecordsLength[iCounter]; - - // increment counter - iCounter++; + uiOffSet = ubFileOffsets[pFilesList->ubCode]; + uiLength = ubFileRecordsLength[pFilesList->ubCode]; } - iLength = ubFileRecordsLength[pFilesList->ubCode]; - - if( pFilesList->ubFormat < ENRICO_BACKGROUND ) - { - - LoadEncryptedDataFromFile("BINARYDATA\\Files.edt", sString, FILE_STRING_SIZE * (iOffSet) * 2, FILE_STRING_SIZE * iLength * 2); - } - - // reset counter - iCounter=0; - // no shadow SetFontShadow(NO_SHADOW); switch( pFilesList->ubFormat ) { - case 0: - + case 0: // no format, all text - - while(iLength > iCounter) + while(uiLength > uiCounter) { - // read one record from file manager file - LoadEncryptedDataFromFile( "BINARYDATA\\Files.edt", sString, FILE_STRING_SIZE * ( iOffSet + iCounter ) * 2, FILE_STRING_SIZE * 2 ); + // read one record from file manager file + LoadEncryptedDataFromFile( "BINARYDATA\\Files.edt", sString, FILE_STRING_SIZE * ( uiOffSet + uiCounter ) * 2, FILE_STRING_SIZE * 2 ); - // display string and get height - iHeight += IanDisplayWrappedString(FILE_VIEWER_X + 4, ( UINT16 )( FILE_VIEWER_Y + iHeight ), FILE_VIEWER_WIDTH, FILE_GAP, FILES_TEXT_FONT, FILE_TEXT_COLOR, sString,0,FALSE,0); + // display string and get height + uiHeight += IanDisplayWrappedString(FILE_VIEWER_X + 4, ( UINT16 )( FILE_VIEWER_Y + uiHeight ), FILE_VIEWER_WIDTH, FILE_GAP, FILES_TEXT_FONT, FILE_TEXT_COLOR, sString,0,FALSE,0); - // increment file record counter - iCounter++; + // increment file record counter + uiCounter++; } - break; + break; - case 1: + case 1: // second format, one picture, all text below @@ -933,26 +1174,26 @@ BOOLEAN DisplayFormattedText( void ) // blt background to screen BltVideoObject(FRAME_BUFFER, hHandle, 0, FILE_VIEWER_X + 4 + ( FILE_VIEWER_WIDTH - usFirstWidth ) / 2, FILE_VIEWER_Y + 10, VO_BLT_SRCTRANSPARENCY,NULL); - iHeight = usFirstHeight + 20; + uiHeight = usFirstHeight + 20; - while(iLength > iCounter) + while(uiLength > uiCounter) { + // read one record from file manager file + LoadEncryptedDataFromFile( "BINARYDATA\\Files.edt", sString, FILE_STRING_SIZE * ( uiOffSet + uiCounter ) * 2, FILE_STRING_SIZE * 2 ); - // read one record from file manager file - LoadEncryptedDataFromFile( "BINARYDATA\\Files.edt", sString, FILE_STRING_SIZE * ( iOffSet + iCounter ) * 2, FILE_STRING_SIZE * 2 ); + // display string and get height + uiHeight += IanDisplayWrappedString(FILE_VIEWER_X + 4, ( UINT16 )( FILE_VIEWER_Y + uiHeight ), FILE_VIEWER_WIDTH, FILE_GAP, FILES_TEXT_FONT, FILE_TEXT_COLOR, sString,0,FALSE,0); - // display string and get height - iHeight += IanDisplayWrappedString(FILE_VIEWER_X + 4, ( UINT16 )( FILE_VIEWER_Y + iHeight ), FILE_VIEWER_WIDTH, FILE_GAP, FILES_TEXT_FONT, FILE_TEXT_COLOR, sString,0,FALSE,0); - - // increment file record counter - iCounter++; + // increment file record counter + uiCounter++; } // delete video object DeleteVideoObjectFromIndex( uiFirstTempPicture ); break; + case 2: // third format, two pictures, side by side with all text below @@ -998,39 +1239,42 @@ BOOLEAN DisplayFormattedText( void ) DeleteVideoObjectFromIndex(uiSecondTempPicture); // put in text - iHeight = usFirstHeight + 20; + uiHeight = usFirstHeight + 20; - while(iLength > iCounter) + while(uiLength > uiCounter) { + // read one record from file manager file + LoadEncryptedDataFromFile( "BINARYDATA\\Files.edt", sString, FILE_STRING_SIZE * ( uiOffSet + uiCounter ) * 2, FILE_STRING_SIZE * 2 ); - // read one record from file manager file - LoadEncryptedDataFromFile( "BINARYDATA\\Files.edt", sString, FILE_STRING_SIZE * ( iOffSet + iCounter ) * 2, FILE_STRING_SIZE * 2 ); + // display string and get height + uiHeight += IanDisplayWrappedString(FILE_VIEWER_X + 4, ( UINT16 )( FILE_VIEWER_Y + uiHeight ), FILE_VIEWER_WIDTH, FILE_GAP, FILES_TEXT_FONT, FILE_TEXT_COLOR, sString,0,FALSE,0); - // display string and get height - iHeight += IanDisplayWrappedString(FILE_VIEWER_X + 4, ( UINT16 )( FILE_VIEWER_Y + iHeight ), FILE_VIEWER_WIDTH, FILE_GAP, FILES_TEXT_FONT, FILE_TEXT_COLOR, sString,0,FALSE,0); - - // increment file record counter - iCounter++; + // increment file record counter + uiCounter++; } break; case 3: - // picture on the left, with text on right and below - // load first graphic + // picture on the left, with text on right and below + // load first graphic HandleSpecialTerroristFile( pFilesList->ubCode, pFilesList->pPicFileNameList[ 0 ] ); - break; + break; + case 4: - // picture on the left, with text on right and below - // load first graphic - HandleMissionBriefingFiles( pFilesList->ubFormat ); - break; + // picture on the left, with text on right and below + // load first graphic + HandleMissionBriefingFiles( pFilesList->ubFormat ); + break; + + case FFORMAT_ADDITIONAL_TEXT: + HandleAdditionalTextFile( pFilesList ); + break; + default: HandleSpecialFiles( pFilesList->ubFormat ); - break; - } HandleFileViewerButtonStates( ); @@ -1264,6 +1508,59 @@ BOOLEAN HandleMissionBriefingFiles( UINT8 ubFormat ) return ( TRUE ); } +static BOOLEAN HandleAdditionalTextFile( FilesUnitPtr file ) +{ + AdditionalFiles_Descriptor *descr = AdditionalFiles_LoadTextFile( file ); + + if ( descr == NULL ) + return FALSE; + + // set up font + INT32 uiFont = AdditionalFiles_GetFontHandler( descr->font ); + UINT8 ubFontColor = descr->textColor; + UINT8 ubFontBgColor = descr->bkgdColor; + UINT32 uiFlags = IAN_WRAP_NO_SHADOW; + + // Create list of width(s). Actually, text file view needs only one of it. + FileRecordWidthPtr widthList = CreateRecordWidth( 0, FILE_VIEWER_WIDTH, 0, 0 ); + FileStringPtr pFileString = GetFirstStringOnThisPage( pFileStringList, + uiFont, FILE_VIEWER_WIDTH, FILE_GAP, giFilesPage, MAX_TEXT_FILE_MESSAGE_PAGE_SIZE, widthList ); + + // move through list and display + UINT16 usPositionOnPage = 0; + while ( pFileString ) + { + UINT16 drawnStringHeight = IanWrappedStringHeight( FILE_VIEWER_X + 4, FILE_VIEWER_Y + usPositionOnPage, + FILE_VIEWER_WIDTH, FILE_GAP, uiFont, ubFontColor, pFileString->pString, ubFontBgColor, FALSE, uiFlags ); + + // if the string we are going to draw fits current page, then draw it, otherwise stop drawing current page + if ( usPositionOnPage + drawnStringHeight < MAX_TEXT_FILE_MESSAGE_PAGE_SIZE ) + { + usPositionOnPage += IanDisplayWrappedString( FILE_VIEWER_X + 4, FILE_VIEWER_Y + usPositionOnPage, + FILE_VIEWER_WIDTH, FILE_GAP, uiFont, ubFontColor, pFileString->pString, ubFontBgColor, FALSE, uiFlags ); + } + else + { + break; + } + + pFileString = pFileString->Next; + } + + if ( pFileString == NULL ) + { + fOnLastFilesPageFlag = TRUE; + } + else + { + fOnLastFilesPageFlag = FALSE; + } + + ClearOutWidthRecordsList( widthList ); + + return TRUE; +} + //------------------------------------------- BOOLEAN HandleSpecialFiles( UINT8 ubFormat ) diff --git a/Laptop/files.h b/Laptop/files.h index fd28da36..af489704 100644 --- a/Laptop/files.h +++ b/Laptop/files.h @@ -43,11 +43,22 @@ enum{ SLAY_BACKGROUND, MATRON_BACKGROUND, IMPOSTER_BACKGROUND, - TIFFANY_BACKGROUND, - REXALL_BACKGROUND, - ELGIN_BACKGROUND, + TIFFANY_BACKGROUND, + REXALL_BACKGROUND, + ELGIN_BACKGROUND, + LAST_JA2_VANILLA_FILE = ELGIN_BACKGROUND, + ADDITIONAL_FILE_0 = 100, + ADDITIONAL_FILE_99 = 199, + ADDITIONAL_FILE_MAX = ADDITIONAL_FILE_99, }; + +// file formats +typedef enum { + FFORMAT_MISSION_BRIEFING = 4, + FFORMAT_ADDITIONAL_TEXT = 5, +} FileFormats; + extern UINT8 ubFileRecordsLength[]; extern BOOLEAN fEnteredFileViewerFromNewFileIcon; extern BOOLEAN fNewFilesInFileViewer; diff --git a/Standard Gaming Platform/FileMan.cpp b/Standard Gaming Platform/FileMan.cpp index 890f71c5..c1630801 100644 --- a/Standard Gaming Platform/FileMan.cpp +++ b/Standard Gaming Platform/FileMan.cpp @@ -57,6 +57,7 @@ using namespace std; #ifdef USE_VFS #include +#include #include struct SOperation @@ -780,6 +781,40 @@ BOOLEAN FileRead( HWFILE hFile, PTR pDest, UINT32 uiBytesToRead, UINT32 *puiByte #endif } +BOOLEAN FileReadLine( HWFILE hFile, std::string* pDest ) +{ +#ifdef USE_VFS + vfs::IBaseFile *pFile = (vfs::IBaseFile*)hFile; + if ( pFile && FileCheckEndOfFile( hFile ) == FALSE && (s_mapFiles[pFile].op == SOperation::READ) ) + { + vfs::tReadableFile *pRF = vfs::tReadableFile::cast( pFile ); + if ( pRF && pDest ) + { + vfs::CReadLine rl( *pRF, false ); + rl.getLine( *pDest ); + return TRUE; + } + } +#endif // ifdef USE_VFS + return FALSE; +} + +BOOLEAN FileReadLine( HWFILE hFile, STR8 pDest, UINT32 uiDestSize, UINT32 *puiBytesRead ) +{ + std::string sBuffer; + BOOLEAN result = (pDest != NULL) && FileReadLine( hFile, &sBuffer ); + if ( result ) + { + if ( puiBytesRead ) + *puiBytesRead = sBuffer.length(); + + UINT32 uiCountToCopy = min( sBuffer.length(), uiDestSize - 1 ); + sBuffer.copy( pDest, uiCountToCopy ); + pDest[uiCountToCopy] = '\0'; // method copy() does not put a null-terminator + } + return result; +} + //************************************************************************** // // FileWrite diff --git a/Standard Gaming Platform/FileMan.h b/Standard Gaming Platform/FileMan.h index 5f51e5de..b35f06fe 100644 --- a/Standard Gaming Platform/FileMan.h +++ b/Standard Gaming Platform/FileMan.h @@ -114,6 +114,8 @@ extern HWFILE FileOpen( STR strFilename, UINT32 uiOptions, BOOLEAN fDeleteOnClos extern void FileClose( HWFILE ); extern BOOLEAN FileRead( HWFILE hFile, PTR pDest, UINT32 uiBytesToRead, UINT32 *puiBytesRead ); +extern BOOLEAN FileReadLine( HWFILE hFile, std::string* pDest ); +extern BOOLEAN FileReadLine( HWFILE hFile, STR8 pDest, UINT32 uiDestSize, UINT32 *puiBytesRead ); extern BOOLEAN FileWrite( HWFILE hFile, PTR pDest, UINT32 uiBytesToWrite, UINT32 *puiBytesWritten ); extern BOOLEAN FileLoad( STR filename, PTR pDest, UINT32 uiBytesToRead, UINT32 *puiBytesRead ); diff --git a/Standard Gaming Platform/Font.cpp b/Standard Gaming Platform/Font.cpp index 37cb4aec..565aa5cc 100644 --- a/Standard Gaming Platform/Font.cpp +++ b/Standard Gaming Platform/Font.cpp @@ -39,7 +39,6 @@ #define PALETTE_SIZE 768 #define STRING_DELIMITER 0 #define ID_BLACK 0 -#define MAX_FONTS 25 //******************************************************* // @@ -282,9 +281,7 @@ void SetRGBFontShadow( UINT16 usFontShadow16 ) //***************************************************************************** BOOLEAN ResetFontObjectPalette(INT32 iFont) { - Assert(iFont >= 0); - Assert(iFont <= MAX_FONTS); - Assert(FontObjs[iFont] !=NULL); + Assert(IsFontLoaded(iFont)); SetFontObjectPalette8BPP(iFont, FontObjs[iFont]->pPaletteEntry); @@ -301,11 +298,8 @@ BOOLEAN ResetFontObjectPalette(INT32 iFont) //***************************************************************************** UINT16 *SetFontObjectPalette8BPP(INT32 iFont, SGPPaletteEntry *pPal8) { -UINT16 *pPal16; - - Assert(iFont >= 0); - Assert(iFont <= MAX_FONTS); - Assert(FontObjs[iFont] !=NULL); + UINT16 *pPal16; + Assert(IsFontLoaded(iFont)); if((pPal16=Create16BPPPalette(pPal8))==NULL) return(NULL); @@ -324,9 +318,7 @@ UINT16 *pPal16; //***************************************************************************** UINT16 *SetFontObjectPalette16BPP(INT32 iFont, UINT16 *pPal16) { - Assert(iFont >= 0); - Assert(iFont <= MAX_FONTS); - Assert(FontObjs[iFont] !=NULL); + Assert(IsFontLoaded(iFont)); FontObjs[iFont]->p16BPPPalette=pPal16; FontObjs[iFont]->pShadeCurrent=pPal16; @@ -343,13 +335,25 @@ UINT16 *SetFontObjectPalette16BPP(INT32 iFont, UINT16 *pPal16) //***************************************************************************** UINT16 *GetFontObjectPalette16BPP(INT32 iFont) { - Assert(iFont >= 0); - Assert(iFont <= MAX_FONTS); - Assert(FontObjs[iFont] !=NULL); + Assert(IsFontLoaded(iFont)); return(FontObjs[iFont]->p16BPPPalette); } +//***************************************************************************** +// IsFontLoaded +// +// Returns TRUE if font at iFont into memory, FALSE otherwise. +// +//***************************************************************************** +BOOLEAN IsFontLoaded(INT32 iFont) +{ + Assert(iFont >= 0); + Assert(iFont <= MAX_FONTS); + + return (FontObjs[iFont] != NULL); +} + //***************************************************************************** // GetFontObject // @@ -358,9 +362,7 @@ UINT16 *GetFontObjectPalette16BPP(INT32 iFont) //***************************************************************************** HVOBJECT GetFontObject(INT32 iFont) { - Assert(iFont >= 0); - Assert(iFont <= MAX_FONTS); - Assert(FontObjs[iFont] !=NULL); + Assert(IsFontLoaded(iFont)); return(FontObjs[iFont]); } @@ -434,9 +436,7 @@ UINT32 LoadIndex; //***************************************************************************** void UnloadFont(UINT32 FontIndex) { - Assert(FontIndex >= 0); - Assert(FontIndex < MAX_FONTS); - Assert(FontObjs[FontIndex]!=NULL); + Assert(IsFontLoaded(FontIndex)); DeleteVideoObject(FontObjs[FontIndex]); FontObjs[FontIndex]=NULL; @@ -728,9 +728,8 @@ UINT32 GetHeight(HVOBJECT hSrcVObject, INT16 ssIndex) //***************************************************************************** UINT16 GetFontHeight(INT32 FontNum) { - Assert(FontNum >= 0); - Assert(FontNum <= MAX_FONTS); - Assert(FontObjs[FontNum]!=NULL); + Assert(IsFontLoaded(FontNum)); + if ( iUseWinFonts ) { INT32 MapFont; MapFont = WinFontMap[FontNum]; @@ -974,9 +973,6 @@ CHAR16 GetUnicodeChar(CHAR16 siChar) //***************************************************************************** BOOLEAN SetFont(INT32 iFontIndex) { - //Assert(iFontIndex >= 0); - //Assert(iFontIndex <= MAX_FONTS); - //Assert(FontObjs[iFontIndex]!=NULL); SGP_THROW_IFFALSE( iFontIndex >= 0 ,"negative font index"); SGP_THROW_IFFALSE( iFontIndex <= MAX_FONTS, "font index > MAX_FONTS" ); SGP_THROW_IFFALSE( FontObjs[iFontIndex]!=NULL, "font is not initialized" ); diff --git a/Standard Gaming Platform/Font.h b/Standard Gaming Platform/Font.h index ef3b8152..07035f57 100644 --- a/Standard Gaming Platform/Font.h +++ b/Standard Gaming Platform/Font.h @@ -9,6 +9,8 @@ //#endif #include "Font Control.h" +#define MAX_FONTS 25 + #define DEFAULT_SHADOW 2 #define MILITARY_SHADOW 67 #define NO_SHADOW 0 @@ -129,6 +131,7 @@ UINT16 *GetFontObjectPalette16BPP(INT32 iFont); void DestroyEnglishTransTable( void ); +extern BOOLEAN IsFontLoaded(INT32 iFont); extern HVOBJECT GetFontObject(INT32 iFont); extern UINT32 gprintf(INT32 x, INT32 y, const STR16 pFontString, ...); extern UINT32 gprintfDirty(INT32 x, INT32 y, const STR16 pFontString, ...); diff --git a/Tactical/XML.h b/Tactical/XML.h index 7fb6b056..3505f1bb 100644 --- a/Tactical/XML.h +++ b/Tactical/XML.h @@ -52,6 +52,7 @@ typedef PARSE_STAGE; #define MAX_CHAR_1000_DATA_LENGTH 1000 #define TABLEDATA_DIRECTORY "TableData\\" +#define TABLEDATA_LAPTOP_DIRECTORY "Laptop\\" #define GERMAN_PREFIX "German." #define RUSSIAN_PREFIX "Russian." @@ -182,6 +183,7 @@ typedef PARSE_STAGE; #define LAPTOPBOBBYRAYSTEXTFILENAME "Laptop\\BobbyRays.xml" #define LAPTOPFLORISTLOCATIONFILENAME "Laptop\\FloristPositions.xml" #define LAPTOPFUNERALLOCATIONFILENAME "Laptop\\FuneralPositions.xml" +#define LAPTOPADDITIONALFILESFILENAME "AdditionalFiles.xml" #define EMAILSENDERNAMELIST "Email\\EmailSenderNameList.xml" #define EMAILMERCAVAILABLE "Email\\EmailMercAvailable.xml" diff --git a/Utils/WordWrap.cpp b/Utils/WordWrap.cpp index 720d6c1f78812f08f005b52b28dd9938fd750500..cc039b85a76f1d391dc01c65a77ccfb0bf300006 100644 GIT binary patch delta 891 zcmah{T}YEr7(VZ}uW$M})~cnnVyvJ-TQN*1q-24NMf2yvtg9hoi65*K64a5xLb|HC zd89W~qO{a@QKJ(bG8(lcx(Mw;f^MX=BqCy6h|*PO-^!dJ-M#1Kd7k&-InR8|__oiO zG|5FLNiAt0he72L) zG#YpA=ugPheM+VEelNc2f_-fAczClw9H}|5263k_Ok+%8l7G^GnQ<`F-iQaM2SH`_ zz%1xeCD`;_+_wbFp-3Yh(n>CncH*SBFK(y7bE)+^R^2=;frh0!;S39D?IcebaUj4N+M5kq{_2$yCvgys&2*fGf7f)Tqm{b56?(r*xMwfA4hnDVNBE1SZ2U%DH!gN?fSWajU&8rCMoB9RjkChmbs(<`>oxfP&MlR`7S6FLW5>fjP{U6IQR zSMAVv-8Q@xf<;UwK%tiY0*sx9qpasiRT)bybZ%X%FFx))voGI()f;SXC6O22*w3oH z|5YD9v>WzwmlwjYPX06VFQ465o-fwGusvU&3-<#K6lyMPz6Rg)@Odx_8NkdaSn(>ms3jOISnnyu~iA&6pZ2m*Sj+&l~| zIYCh=9YEWbim5wd!SCZxiE|@Rz|A+IQqG?0&znv8 ze~QCyi2<_ETx`Rl!+KO}b~5*&vD755)WUMg_pSk$+j&4nz2An9Ja%-f>|noZ7Xb5b z%UI=1r^q{y*=l_OfERNcK1I(fH1HOT?C$B4I*Md~ObZ!M%_^GabkQ{5QU)t(sWxDs zRzb60iR-nqkDi&NG*MnEky<5})F$Oh4oQ`S2U9v)G2CE8wS9g=L|07GHuj_=9ilSc zpZ}uME;!WL6m_Kd_9aoI6cntfGsOjk&{bz*dHxeX4<^7&BgbGXc-hlS&ooUBJJXoE z>v92jIb}gP(4+^9m_MsWH#)F#s0za&Jr2B7*ymUMxtc!aTRLY=D8d{o#kzIm0HjQS zh33j2nApskUxnGRYNd89!WWM~J0uW_F@$*GQ8=~!$s4lr>*dg&DQI(A;JK>NlAz5a zejp@ya#!(7iP3j?JfA37$Om}pzEuc%`TZw5nL5G5)@}~ z{*0NX??HcWokD?e(Y9fa4a1p6wEWQHnF1`WqQ ejDt%TtA?si&nwgm6>!o|N+lm7sCgetSize(); + vfs::size_t startReadPosition = _file.getReadPosition(); + _bytes_left = startReadPosition >= rfile->getSize() ? 0 : rfile->getSize() - startReadPosition; fillBuffer(); - vfs::UByte utf8bom[3] = {0xef,0xbb,0xbf}; - if(memcmp(utf8bom, &_buffer[0],3) == 0) + + if (startReadPosition == 0) { - _buffer_pos += 3; + vfs::UByte utf8bom[3] = { 0xef, 0xbb, 0xbf }; + if (memcmp( utf8bom, &_buffer[0], 3) == 0 ) + { + _buffer_pos += 3; + } } rfile.release(); }; vfs::CReadLine::~CReadLine() { - if(_file.isOpenRead()) + if (_auto_ctrl_file && _file.isOpenRead()) { _file.close(); } } -bool vfs::CReadLine::fillBuffer() +bool vfs::CReadLine::fillBuffer(bool refill) { - if(_eof) + if (_bytes_left == 0 || (_auto_ctrl_file == false && refill == true)) { return false; } + vfs::size_t bytesRead = BUFFER_SIZE < _bytes_left ? BUFFER_SIZE : _bytes_left; try { @@ -79,7 +85,6 @@ bool vfs::CReadLine::fillBuffer() } _bytes_left -= bytesRead; - _eof = (_bytes_left == 0); // bite-wise read files usually terminate a line with \n (or \r\n on WIN32) @@ -136,7 +141,7 @@ bool vfs::CReadLine::fromBuffer(std::string& line) } else { - done = !fillBuffer(); + done = !fillBuffer(true); } } else if(*temp == '\n' || *temp == 0) @@ -148,12 +153,12 @@ bool vfs::CReadLine::fromBuffer(std::string& line) } else { - done = !fillBuffer(); + done = !fillBuffer(true); } } else { - done = !fillBuffer(); + done = !fillBuffer(true); } } return false; @@ -162,7 +167,16 @@ bool vfs::CReadLine::fromBuffer(std::string& line) bool vfs::CReadLine::getLine(std::string& line) { line.clear(); - return fromBuffer(line); + bool gotLine = fromBuffer(line); + + // If file handler is controlled by caller, we have to read to EOL or EOF. So if happened to read over EOL (due to buffering), + // then we need to move caret back to a position where just fetched line ends (EOL). + if (_auto_ctrl_file == false && _buffer_pos < _buffer_last) + { + vfs::offset_t offset = (vfs::offset_t)_buffer_pos - _buffer_last; // "lesser - greater" intentionally to get negative offset + _file.setReadPosition(offset, _file.SD_CURRENT); + } + return gotLine; } /*************************************************************************************/