diff --git a/Editor/EditorMapInfo.cpp b/Editor/EditorMapInfo.cpp index b0a3db91..600cd83b 100644 --- a/Editor/EditorMapInfo.cpp +++ b/Editor/EditorMapInfo.cpp @@ -227,7 +227,7 @@ void ExtractAndUpdateMapInfo() gMapInformation.ubRestrictedScrollID = (UINT8)temp; //set up fields for exitgrid information - Get16BitStringFromField( 7, str ); + Get16BitStringFromField( 7, str, 10 ); if( str[0] >= 'a' && str[0] <= 'z' ) str[0] -= 32; //uppercase it! if( str[0] >= 'A' && str[0] <= 'Z' && diff --git a/Editor/LoadScreen.cpp b/Editor/LoadScreen.cpp index 6a70e1b8..60cd8401 100644 --- a/Editor/LoadScreen.cpp +++ b/Editor/LoadScreen.cpp @@ -1068,7 +1068,7 @@ void FDlgDwnCallback( GUI_BUTTON *butn, INT32 reason ) BOOLEAN ExtractFilenameFromFields() { - Get16BitStringFromField( 0, gzFilename ); + Get16BitStringFromField( 0, gzFilename, 31 ); return ValidFilename(); } diff --git a/Editor/Sector Summary.cpp b/Editor/Sector Summary.cpp index 42907b25..7df52321 100644 --- a/Editor/Sector Summary.cpp +++ b/Editor/Sector Summary.cpp @@ -2787,7 +2787,7 @@ void SummaryUpdateCallback( GUI_BUTTON *btn, INT32 reason ) void ExtractTempFilename() { CHAR16 str[40]; - Get16BitStringFromField( 1, str ); + Get16BitStringFromField( 1, str, 40 ); if( wcscmp( gszTempFilename, str ) ) { wcscpy( gszTempFilename, str ); diff --git a/GameSettings.cpp b/GameSettings.cpp index f6831544..948e0ef8 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -40,6 +40,10 @@ #include "Text.h" #include "connect.h" +#include "VFS/vfs.h" +#include "VFS/vfs_file_raii.h" +#include "VFS/File/vfs_file.h" + #define GAME_SETTINGS_FILE "Ja2_Settings.INI" #define GAME_INI_FILE "..\\Ja2.ini" @@ -89,6 +93,7 @@ BOOLEAN IsNIVModeValid(bool checkRes) { if(iResolution == 0 && checkRes == true) return( FALSE ); +#ifndef USE_VFS if(gCustomDataCat.GetRootDir() == "") return( FALSE ); char customDataPath[MAX_PATH]; @@ -96,6 +101,14 @@ BOOLEAN IsNIVModeValid(bool checkRes) if(StringToLower((std::string)customDataPath) == "data") return( FALSE ); return( TRUE ); +#else + // Check if the Profile with the NAME = "v1.13" is found in the specificed vfs_config.*.ini + if(GetVFS()->GetProfileStack()->GetProfile(L"v1.13") != NULL) + { + return TRUE; + } + return FALSE; +#endif } @@ -230,124 +243,146 @@ BOOLEAN LoadGameSettings() BOOLEAN SaveGameSettings() { - char gameSettingsFilePath[MAX_PATH]; - FILE *file_pointer; - //Record the current settings into the game settins structure gGameSettings.ubSoundEffectsVolume = (UINT8)GetSoundEffectsVolume( ); gGameSettings.ubSpeechVolume = (UINT8)GetSpeechVolume( ); gGameSettings.ubMusicVolumeSetting = (UINT8)MusicGetVolume( ); +#ifndef USE_VFS + char gameSettingsFilePath[MAX_PATH]; + FILE *file_pointer; sprintf(gameSettingsFilePath, "%s\\%s", gCustomDataCat.GetRootDir().c_str(), GAME_SETTINGS_FILE); - fopen_s( &file_pointer, gameSettingsFilePath, "w" ); - - if( file_pointer ) + if( !file_pointer ) { - fprintf_s (file_pointer , ";******************************************************************************************************************************\n" ); - fprintf_s (file_pointer , ";******************************************************************************************************************************\n" ); - fprintf_s (file_pointer , "; Jagged Alliance 2 --Settings File-- \n" ); - fprintf_s (file_pointer , "; \n" ); - fprintf_s (file_pointer , "; Please note that this file is automatically generated by the game. \n" ); - fprintf_s (file_pointer , "; \n" ); - fprintf_s (file_pointer , "; While it is safe to change things from within this file, not all values are acceptable. Some may break the game, \n" ); - fprintf_s (file_pointer , "; some may be ignored, but most likely they will be acceptable or reset to a default value. \n" ); - fprintf_s (file_pointer , "; \n" ); - fprintf_s (file_pointer , "; Please note, This file and its contents are in a beta phase. Expect changes, however they should be minimal and automated.\n" ); - fprintf_s (file_pointer , "; \n" ); - fprintf_s (file_pointer , ";******************************************************************************************************************************\n" ); - fprintf_s (file_pointer , "\n" ); - fprintf_s (file_pointer , ";******************************************************************************************************************************\n" ); - fprintf_s (file_pointer , ";\n" ); - fprintf_s (file_pointer , "; The Current Game Setting Struct is defined as : \n" ); - fprintf_s (file_pointer , ";\n" ); - fprintf_s (file_pointer , "; typedef struct \n"); - fprintf_s (file_pointer , "; { \n"); - fprintf_s (file_pointer , "; INT8 bLastSavedGameSlot; // The last saved game number goes in here \n"); - fprintf_s (file_pointer , "; UINT8 ubMusicVolumeSetting; // Volume Setting \n"); - fprintf_s (file_pointer , "; UINT8 ubSoundEffectsVolume; // Volume Setting \n"); - fprintf_s (file_pointer , "; UINT8 ubSpeechVolume; // Volume Setting \n"); - fprintf_s (file_pointer , "; UINT8 fOptions[ NUM_ALL_GAME_OPTIONS ]; // Toggle Options (Speech, Subtitles, Show Tree Tops, etc.. ) \n"); - fprintf_s (file_pointer , "; UINT32 uiMeanwhileScenesSeenFlags; // Bit Vector describing seen 'mean whiles..' \n"); - fprintf_s (file_pointer , "; BOOLEAN fHideHelpInAllScreens; // Controls Help \"do not show help again\" checkbox \n"); - fprintf_s (file_pointer , "; UINT8 ubSizeOfDisplayCover; // The number of grids the player designates thru [Delete + ( = or - )] \n"); - fprintf_s (file_pointer , "; UINT8 ubSizeOfLOS; // The number of grids the player designates thru [End + ( = or - )] \n"); - fprintf_s (file_pointer , "; } GAME_SETTINGS; \n"); - fprintf_s (file_pointer , ";\n" ); - fprintf_s (file_pointer , ";******************************************************************************************************************************\n" ); - fprintf_s (file_pointer , "\n\n" ); - fprintf_s (file_pointer , "[JA2 Game Settings]\n" ); - - - fprintf_s (file_pointer , "bLastSavedGameSlot = %d \n" , gGameSettings.bLastSavedGameSlot ); - fprintf_s (file_pointer , "ubMusicVolumeSetting = %u \n" , gGameSettings.ubMusicVolumeSetting ); - fprintf_s (file_pointer , "ubSoundEffectsVolume = %u \n" , gGameSettings.ubSoundEffectsVolume ); - fprintf_s (file_pointer , "ubSpeechVolume = %u \n" , gGameSettings.ubSpeechVolume ); - fprintf_s (file_pointer , "uiMeanwhileScenesSeenFlags = %u \n" , gGameSettings.uiMeanwhileScenesSeenFlags ); - fprintf_s (file_pointer , "fHideHelpInAllScreens = %s \n" , (gGameSettings.fHideHelpInAllScreens ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "ubSizeOfDisplayCover = %u \n" , gGameSettings.ubSizeOfDisplayCover ); - fprintf_s (file_pointer , "ubSizeOfLOS = %u \n" , gGameSettings.ubSizeOfLOS ); - fprintf_s (file_pointer , "TOPTION_SPEECH = %s \n" , (gGameSettings.fOptions[TOPTION_SPEECH] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_MUTE_CONFIRMATIONS = %s \n" , (gGameSettings.fOptions[TOPTION_MUTE_CONFIRMATIONS] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_SUBTITLES = %s \n" , (gGameSettings.fOptions[TOPTION_SUBTITLES] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_KEY_ADVANCE_SPEECH = %s \n" , (gGameSettings.fOptions[TOPTION_KEY_ADVANCE_SPEECH] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_ANIMATE_SMOKE = %s \n" , (gGameSettings.fOptions[TOPTION_ANIMATE_SMOKE] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_BLOOD_N_GORE = %s \n" , (gGameSettings.fOptions[TOPTION_BLOOD_N_GORE] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_DONT_MOVE_MOUSE = %s \n" , (gGameSettings.fOptions[TOPTION_DONT_MOVE_MOUSE] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_OLD_SELECTION_METHOD = %s \n" , (gGameSettings.fOptions[TOPTION_OLD_SELECTION_METHOD] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_ALWAYS_SHOW_MOVEMENT_PATH = %s \n" , (gGameSettings.fOptions[TOPTION_ALWAYS_SHOW_MOVEMENT_PATH] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_SHOW_MISSES = %s \n" , (gGameSettings.fOptions[TOPTION_SHOW_MISSES] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_RTCONFIRM = %s \n" , (gGameSettings.fOptions[TOPTION_RTCONFIRM] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_SLEEPWAKE_NOTIFICATION = %s \n" , (gGameSettings.fOptions[TOPTION_SLEEPWAKE_NOTIFICATION] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_USE_METRIC_SYSTEM = %s \n" , (gGameSettings.fOptions[TOPTION_USE_METRIC_SYSTEM] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_MERC_ALWAYS_LIGHT_UP = %s \n" , (gGameSettings.fOptions[TOPTION_MERC_ALWAYS_LIGHT_UP] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_SMART_CURSOR = %s \n" , (gGameSettings.fOptions[TOPTION_SMART_CURSOR] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_SNAP_CURSOR_TO_DOOR = %s \n" , (gGameSettings.fOptions[TOPTION_SNAP_CURSOR_TO_DOOR] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_GLOW_ITEMS = %s \n" , (gGameSettings.fOptions[TOPTION_GLOW_ITEMS] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_TOGGLE_TREE_TOPS = %s \n" , (gGameSettings.fOptions[TOPTION_TOGGLE_TREE_TOPS] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_TOGGLE_WIREFRAME = %s \n" , (gGameSettings.fOptions[TOPTION_TOGGLE_WIREFRAME] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_3D_CURSOR = %s \n" , (gGameSettings.fOptions[TOPTION_3D_CURSOR] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_CTH_CURSOR = %s \n" , (gGameSettings.fOptions[TOPTION_CTH_CURSOR] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_GL_BURST_CURSOR = %s \n" , (gGameSettings.fOptions[TOPTION_GL_BURST_CURSOR] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_DROP_ALL = %s \n" , (gGameSettings.fOptions[TOPTION_DROP_ALL] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_GL_HIGH_ANGLE = %s \n" , (gGameSettings.fOptions[TOPTION_GL_HIGH_ANGLE] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_AIM_LEVEL_RESTRICTION = %s \n" , (gGameSettings.fOptions[TOPTION_AIM_LEVEL_RESTRICTION] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_SPACE_SELECTS_NEXT_SQUAD = %s \n" , (gGameSettings.fOptions[TOPTION_SPACE_SELECTS_NEXT_SQUAD] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_SHOW_ITEM_SHADOW = %s \n" , (gGameSettings.fOptions[TOPTION_SHOW_ITEM_SHADOW] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_SHOW_WEAPON_RANGE_IN_TILES = %s \n" , (gGameSettings.fOptions[TOPTION_SHOW_WEAPON_RANGE_IN_TILES] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_TRACERS_FOR_SINGLE_FIRE = %s \n" , (gGameSettings.fOptions[TOPTION_TRACERS_FOR_SINGLE_FIRE] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_RAIN_SOUND = %s \n" , (gGameSettings.fOptions[TOPTION_RAIN_SOUND] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_ALLOW_CROWS = %s \n" , (gGameSettings.fOptions[TOPTION_ALLOW_CROWS] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_ALLOW_SOLDIER_TOOLTIPS = %s \n" , (gGameSettings.fOptions[TOPTION_ALLOW_SOLDIER_TOOLTIPS] ? "TRUE" : "FALSE" ) ); // changed from random IMP personality - SANDRO - fprintf_s (file_pointer , "TOPTION_USE_AUTO_SAVE = %s \n" , (gGameSettings.fOptions[TOPTION_USE_AUTO_SAVE] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_SILENT_SKYRIDER = %s \n" , (gGameSettings.fOptions[TOPTION_SILENT_SKYRIDER] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_LOW_CPU_USAGE = %s \n" , (gGameSettings.fOptions[TOPTION_LOW_CPU_USAGE] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_ENHANCED_DESC_BOX = %s \n" , (gGameSettings.fOptions[TOPTION_ENHANCED_DESC_BOX] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_TOGGLE_TURN_MODE = %s \n" , (gGameSettings.fOptions[TOPTION_TOGGLE_TURN_MODE] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_CHEAT_MODE_OPTIONS_HEADER = %s \n" , (gGameSettings.fOptions[TOPTION_CHEAT_MODE_OPTIONS_HEADER] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_FORCE_BOBBY_RAY_SHIPMENTS = %s \n" , (gGameSettings.fOptions[TOPTION_FORCE_BOBBY_RAY_SHIPMENTS] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_CHEAT_MODE_OPTIONS_END = %s \n" , (gGameSettings.fOptions[TOPTION_CHEAT_MODE_OPTIONS_END] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_DEBUG_MODE_OPTIONS_HEADER = %s \n" , (gGameSettings.fOptions[TOPTION_DEBUG_MODE_OPTIONS_HEADER] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_SHOW_RESET_ALL_OPTIONS = %s \n" , (gGameSettings.fOptions[TOPTION_SHOW_RESET_ALL_OPTIONS] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_RESET_ALL_OPTIONS = %s \n" , (gGameSettings.fOptions[TOPTION_RESET_ALL_OPTIONS] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_RETAIN_DEBUG_OPTIONS_IN_RELEASE = %s \n" , (gGameSettings.fOptions[TOPTION_RETAIN_DEBUG_OPTIONS_IN_RELEASE] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_DEBUG_MODE_RENDER_OPTIONS_GROUP = %s \n" , (gGameSettings.fOptions[TOPTION_DEBUG_MODE_RENDER_OPTIONS_GROUP] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_RENDER_MOUSE_REGIONS = %s \n" , (gGameSettings.fOptions[TOPTION_RENDER_MOUSE_REGIONS] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_DEBUG_MODE_OPTIONS_END = %s \n" , (gGameSettings.fOptions[TOPTION_DEBUG_MODE_OPTIONS_END] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , ";******************************************************************************************************************************\n" ); - fprintf_s (file_pointer , "; Options beyond this point are Code derived options, DO NOT CHANGE THESE UNLESS YOU KNOW WHAT YOUR ARE DOING. \n" ); - fprintf_s (file_pointer , "; They are only included here for complete transparency for all GameSettings content. \n" ); - fprintf_s (file_pointer , ";******************************************************************************************************************************\n" ); - fprintf_s (file_pointer , "TOPTION_LAST_OPTION = %s \n" , (gGameSettings.fOptions[TOPTION_LAST_OPTION] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "NUM_GAME_OPTIONS = %s \n" , (gGameSettings.fOptions[NUM_GAME_OPTIONS] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_MERC_CASTS_LIGHT = %s \n" , (gGameSettings.fOptions[TOPTION_MERC_CASTS_LIGHT] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_HIDE_BULLETS = %s \n" , (gGameSettings.fOptions[TOPTION_HIDE_BULLETS] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "TOPTION_TRACKING_MODE = %s \n" , (gGameSettings.fOptions[TOPTION_TRACKING_MODE] ? "TRUE" : "FALSE" ) ); - fprintf_s (file_pointer , "NUM_ALL_GAME_OPTIONS = %s \n" , (gGameSettings.fOptions[NUM_ALL_GAME_OPTIONS] ? "TRUE" : "FALSE" ) ); - + //?fclose( file_pointer ); + return FALSE; } +#endif + + std::stringstream settings; + const char endl[] = "\r\n"; + settings << ";******************************************************************************************************************************" << endl; + settings << ";******************************************************************************************************************************" << endl; + settings << "; Jagged Alliance 2 --Settings File-- " << endl; + settings << "; " << endl; + settings << "; Please note that this file is automatically generated by the game. " << endl; + settings << "; " << endl; + settings << "; While it is safe to change things from within this file, not all values are acceptable. Some may break the game, " << endl; + settings << "; some may be ignored, but most likely they will be acceptable or reset to a default value. " << endl; + settings << "; " << endl; + settings << "; Please note, This file and its contents are in a beta phase. Expect changes, however they should be minimal and automated." << endl; + settings << "; " << endl; + settings << ";******************************************************************************************************************************" << endl; + settings << endl; + settings << ";******************************************************************************************************************************" << endl; + settings << endl; + settings << "; The Current Game Setting Struct is defined as : " << endl; + settings << ";" << endl; + settings << "; typedef struct" << endl; + settings << "; {" << endl; + settings << "; INT8 bLastSavedGameSlot; // The last saved game number goes in here" << endl; + settings << "; UINT8 ubMusicVolumeSetting; // Volume Setting" << endl; + settings << "; UINT8 ubSoundEffectsVolume; // Volume Setting" << endl; + settings << "; UINT8 ubSpeechVolume; // Volume Setting" << endl; + settings << "; UINT8 fOptions[ NUM_ALL_GAME_OPTIONS ]; // Toggle Options (Speech, Subtitles, Show Tree Tops, etc.. )" << endl; + settings << "; UINT32 uiMeanwhileScenesSeenFlags; // Bit Vector describing seen 'mean whiles..'" << endl; + settings << "; BOOLEAN fHideHelpInAllScreens; // Controls Help \"do not show help again\" checkbox" << endl; + settings << "; UINT8 ubSizeOfDisplayCover; // The number of grids the player designates thru [Delete + ( = or - )]" << endl; + settings << "; UINT8 ubSizeOfLOS; // The number of grids the player designates thru [End + ( = or - )]" << endl; + settings << "; } GAME_SETTINGS" << endl; + settings << ";" << endl; + settings << ";******************************************************************************************************************************" << endl; + settings << endl << endl; + settings << "[JA2 Game Settings]" << endl; + + settings << "bLastSavedGameSlot = " << (int)gGameSettings.bLastSavedGameSlot << endl; + settings << "ubMusicVolumeSetting = " << (int)gGameSettings.ubMusicVolumeSetting << endl; + settings << "ubSoundEffectsVolume = " << (int)gGameSettings.ubSoundEffectsVolume << endl; + settings << "ubSpeechVolume = " << (int)gGameSettings.ubSpeechVolume << endl; + settings << "uiMeanwhileScenesSeenFlags = " << gGameSettings.uiMeanwhileScenesSeenFlags << endl; + settings << "fHideHelpInAllScreens = " << (gGameSettings.fHideHelpInAllScreens ? "TRUE" : "FALSE" ) << endl; + settings << "ubSizeOfDisplayCover = " << (int)gGameSettings.ubSizeOfDisplayCover << endl; + settings << "ubSizeOfLOS = " << (int)gGameSettings.ubSizeOfLOS << endl; + settings << "TOPTION_SPEECH = " << (gGameSettings.fOptions[TOPTION_SPEECH] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_MUTE_CONFIRMATIONS = " << (gGameSettings.fOptions[TOPTION_MUTE_CONFIRMATIONS] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_SUBTITLES = " << (gGameSettings.fOptions[TOPTION_SUBTITLES] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_KEY_ADVANCE_SPEECH = " << (gGameSettings.fOptions[TOPTION_KEY_ADVANCE_SPEECH] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_ANIMATE_SMOKE = " << (gGameSettings.fOptions[TOPTION_ANIMATE_SMOKE] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_BLOOD_N_GORE = " << (gGameSettings.fOptions[TOPTION_BLOOD_N_GORE] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_DONT_MOVE_MOUSE = " << (gGameSettings.fOptions[TOPTION_DONT_MOVE_MOUSE] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_OLD_SELECTION_METHOD = " << (gGameSettings.fOptions[TOPTION_OLD_SELECTION_METHOD] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_ALWAYS_SHOW_MOVEMENT_PATH = " << (gGameSettings.fOptions[TOPTION_ALWAYS_SHOW_MOVEMENT_PATH] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_SHOW_MISSES = " << (gGameSettings.fOptions[TOPTION_SHOW_MISSES] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_RTCONFIRM = " << (gGameSettings.fOptions[TOPTION_RTCONFIRM] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_SLEEPWAKE_NOTIFICATION = " << (gGameSettings.fOptions[TOPTION_SLEEPWAKE_NOTIFICATION] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_USE_METRIC_SYSTEM = " << (gGameSettings.fOptions[TOPTION_USE_METRIC_SYSTEM] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_MERC_ALWAYS_LIGHT_UP = " << (gGameSettings.fOptions[TOPTION_MERC_ALWAYS_LIGHT_UP] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_SMART_CURSOR = " << (gGameSettings.fOptions[TOPTION_SMART_CURSOR] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_SNAP_CURSOR_TO_DOOR = " << (gGameSettings.fOptions[TOPTION_SNAP_CURSOR_TO_DOOR] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_GLOW_ITEMS = " << (gGameSettings.fOptions[TOPTION_GLOW_ITEMS] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_TOGGLE_TREE_TOPS = " << (gGameSettings.fOptions[TOPTION_TOGGLE_TREE_TOPS] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_TOGGLE_WIREFRAME = " << (gGameSettings.fOptions[TOPTION_TOGGLE_WIREFRAME] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_3D_CURSOR = " << (gGameSettings.fOptions[TOPTION_3D_CURSOR] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_CTH_CURSOR = " << (gGameSettings.fOptions[TOPTION_CTH_CURSOR] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_GL_BURST_CURSOR = " << (gGameSettings.fOptions[TOPTION_GL_BURST_CURSOR] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_DROP_ALL = " << (gGameSettings.fOptions[TOPTION_DROP_ALL] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_GL_HIGH_ANGLE = " << (gGameSettings.fOptions[TOPTION_GL_HIGH_ANGLE] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_AIM_LEVEL_RESTRICTION = " << (gGameSettings.fOptions[TOPTION_AIM_LEVEL_RESTRICTION] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_SPACE_SELECTS_NEXT_SQUAD = " << (gGameSettings.fOptions[TOPTION_SPACE_SELECTS_NEXT_SQUAD] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_SHOW_ITEM_SHADOW = " << (gGameSettings.fOptions[TOPTION_SHOW_ITEM_SHADOW] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_SHOW_WEAPON_RANGE_IN_TILES = " << (gGameSettings.fOptions[TOPTION_SHOW_WEAPON_RANGE_IN_TILES] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_TRACERS_FOR_SINGLE_FIRE = " << (gGameSettings.fOptions[TOPTION_TRACERS_FOR_SINGLE_FIRE] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_RAIN_SOUND = " << (gGameSettings.fOptions[TOPTION_RAIN_SOUND] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_ALLOW_CROWS = " << (gGameSettings.fOptions[TOPTION_ALLOW_CROWS] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_ALLOW_SOLDIER_TOOLTIPS = " << (gGameSettings.fOptions[TOPTION_ALLOW_SOLDIER_TOOLTIPS] ? "TRUE" : "FALSE" ) << endl; // changed from random IMP personality - SANDRO + settings << "TOPTION_USE_AUTO_SAVE = " << (gGameSettings.fOptions[TOPTION_USE_AUTO_SAVE] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_SILENT_SKYRIDER = " << (gGameSettings.fOptions[TOPTION_SILENT_SKYRIDER] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_LOW_CPU_USAGE = " << (gGameSettings.fOptions[TOPTION_LOW_CPU_USAGE] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_ENHANCED_DESC_BOX = " << (gGameSettings.fOptions[TOPTION_ENHANCED_DESC_BOX] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_TOGGLE_TURN_MODE = " << (gGameSettings.fOptions[TOPTION_TOGGLE_TURN_MODE] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_CHEAT_MODE_OPTIONS_HEADER = " << (gGameSettings.fOptions[TOPTION_CHEAT_MODE_OPTIONS_HEADER] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_FORCE_BOBBY_RAY_SHIPMENTS = " << (gGameSettings.fOptions[TOPTION_FORCE_BOBBY_RAY_SHIPMENTS] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_CHEAT_MODE_OPTIONS_END = " << (gGameSettings.fOptions[TOPTION_CHEAT_MODE_OPTIONS_END] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_DEBUG_MODE_OPTIONS_HEADER = " << (gGameSettings.fOptions[TOPTION_DEBUG_MODE_OPTIONS_HEADER] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_SHOW_RESET_ALL_OPTIONS = " << (gGameSettings.fOptions[TOPTION_SHOW_RESET_ALL_OPTIONS] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_RESET_ALL_OPTIONS = " << (gGameSettings.fOptions[TOPTION_RESET_ALL_OPTIONS] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_RETAIN_DEBUG_OPTIONS_IN_RELEASE = " << (gGameSettings.fOptions[TOPTION_RETAIN_DEBUG_OPTIONS_IN_RELEASE] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_DEBUG_MODE_RENDER_OPTIONS_GROUP = " << (gGameSettings.fOptions[TOPTION_DEBUG_MODE_RENDER_OPTIONS_GROUP] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_RENDER_MOUSE_REGIONS = " << (gGameSettings.fOptions[TOPTION_RENDER_MOUSE_REGIONS] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_DEBUG_MODE_OPTIONS_END = " << (gGameSettings.fOptions[TOPTION_DEBUG_MODE_OPTIONS_END] ? "TRUE" : "FALSE" ) << endl; + settings << ";******************************************************************************************************************************" << endl; + settings << "; Options beyond this point are Code derived options, DO NOT CHANGE THESE UNLESS YOU KNOW WHAT YOUR ARE DOING." << endl; + settings << "; They are only included here for complete transparency for all GameSettings content. " << endl; + settings << ";******************************************************************************************************************************" << endl; + settings << "TOPTION_LAST_OPTION = " << (gGameSettings.fOptions[TOPTION_LAST_OPTION] ? "TRUE" : "FALSE" ) << endl; + settings << "NUM_GAME_OPTIONS = " << (gGameSettings.fOptions[NUM_GAME_OPTIONS] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_MERC_CASTS_LIGHT = " << (gGameSettings.fOptions[TOPTION_MERC_CASTS_LIGHT] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_HIDE_BULLETS = " << (gGameSettings.fOptions[TOPTION_HIDE_BULLETS] ? "TRUE" : "FALSE" ) << endl; + settings << "TOPTION_TRACKING_MODE = " << (gGameSettings.fOptions[TOPTION_TRACKING_MODE] ? "TRUE" : "FALSE" ) << endl; + settings << "NUM_ALL_GAME_OPTIONS = " << (gGameSettings.fOptions[NUM_ALL_GAME_OPTIONS] ? "TRUE" : "FALSE" ) << endl; + +#ifndef USE_VFS + fprintf_s (file_pointer , settings.str().c_str()); fclose( file_pointer ); +#else + vfs::UInt32 written; + try + { + vfs::COpenWriteFile wfile(GAME_SETTINGS_FILE,true,true); + wfile.file().Write(settings.str().c_str(), settings.str().length(),written); + } + catch(CBasicException& ex) + { + vfs::CFile wfile(GAME_SETTINGS_FILE); + if(wfile.OpenWrite(true,true)) + { + wfile.Write(settings.str().c_str(), settings.str().length(),written); + wfile.Close(); + } + } +#endif return( TRUE ); @@ -508,9 +543,13 @@ void LoadGameExternalOptions() // WANNE: FILE TRANSFER: We disable reading from ja2_options.ini until we get file transfer working - if(is_networked) - memset(&iniReader, 0, sizeof (CIniReader) );//disable ini in mp (taking default values) - + if(is_networked) + { +#ifndef USE_VFS + //memset(&iniReader, 0, sizeof (CIniReader) );//disable ini in mp (taking default values) + iniReader.Clear(); +#endif + } //################# Laptop Settings ################# @@ -546,10 +585,14 @@ void LoadGameExternalOptions() if (gGameExternalOptions.iaIMPSlots[idx] < 0) { if (idx < COUNT_STANDARD_MALE_SLOTS) + { gGameExternalOptions.iaIMPSlots[idx] = FIRST_STANDARD_MALE_SLOT+idx; + } else + { // This is bad so just use the last standard slot # gGameExternalOptions.iaIMPSlots[idx] = FIRST_STANDARD_MALE_SLOT+COUNT_STANDARD_MALE_SLOTS-1; + } } } gGameExternalOptions.iaIMPSlots[gGameExternalOptions.iIMPMaleCharacterCount] = -1; @@ -560,17 +603,21 @@ void LoadGameExternalOptions() if (gGameExternalOptions.iaIMPSlots[idx+gGameExternalOptions.iIMPMaleCharacterCount+1] < 0) { if (idx < COUNT_STANDARD_FEMALE_SLOTS) + { gGameExternalOptions.iaIMPSlots[idx+gGameExternalOptions.iIMPMaleCharacterCount+1] = FIRST_STANDARD_FEMALE_SLOT+idx; + } else + { // This is bad so just use the last standard slot # gGameExternalOptions.iaIMPSlots[idx+gGameExternalOptions.iIMPMaleCharacterCount+1] = FIRST_STANDARD_FEMALE_SLOT+COUNT_STANDARD_FEMALE_SLOTS-1; + } } } gGameExternalOptions.iaIMPSlots[gGameExternalOptions.iIMPFemaleCharacterCount+gGameExternalOptions.iIMPMaleCharacterCount+1] = -1; //Character generation - gGameExternalOptions.iMinAttribute = iniReader.ReadInteger("JA2 Laptop Settings","MIN_ATTRIBUTE_POINT",35, 1, 99); - gGameExternalOptions.iMaxAttribute = iniReader.ReadInteger("JA2 Laptop Settings","MAX_ATTRIBUTE_POINT",90, gGameExternalOptions.iMinAttribute+1, 100); + gGameExternalOptions.iMinAttribute = iniReader.ReadInteger("JA2 Laptop Settings","MIN_ATTRIBUTE_POINT",35, 1, 99); + gGameExternalOptions.iMaxAttribute = iniReader.ReadInteger("JA2 Laptop Settings","MAX_ATTRIBUTE_POINT",90, gGameExternalOptions.iMinAttribute+1, 100); gGameExternalOptions.iImpAttributePoints = iniReader.ReadInteger("JA2 Laptop Settings","IMPATTRIBUTEPOINTS",40, 1, 100); gGameExternalOptions.iMaxZeroBonus = iniReader.ReadInteger("JA2 Laptop Settings","MAX_ZERO_BONUS",15, 0, 100); gGameExternalOptions.iStartAttribute = iniReader.ReadInteger("JA2 Laptop Settings","START_ATTRIBUTE",55, 1, 100); @@ -579,7 +626,6 @@ void LoadGameExternalOptions() // These two removed - SANDRO //gGameExternalOptions.iCustomPersonality = (INT8) iniReader.ReadInteger("JA2 Laptop Settings","CUSTOM_PERSONALITY",0, 0, NUM_PERSONALITYTRAIT-1); //gGameExternalOptions.iCustomAttitude = (INT8) iniReader.ReadInteger("JA2 Laptop Settings","CUSTOM_ATTITUDE",0, 0, NUM_ATTITUDES-1); - // These three added - SANDRO gGameExternalOptions.iIMPStartingLevelCostMultiplier = iniReader.ReadInteger("JA2 Laptop Settings","IMP_STARTING_LEVEL_COST_MULTIPLIER", 5, 0, 100); gGameExternalOptions.iBonusPointsForDisability = iniReader.ReadInteger("JA2 Laptop Settings","IMP_BONUS_POINTS_FOR_DISABILITY",20, 0, 500); @@ -592,17 +638,17 @@ void LoadGameExternalOptions() //Merc Death Settings: gGameExternalOptions.gfMercsDieOnAssignment = iniReader.ReadBoolean("JA2 Laptop Settings","MERCS_DIE_ON_ASSIGNMENT",TRUE); gGameExternalOptions.giEasyMercDeaths = iniReader.ReadInteger("JA2 Laptop Settings","EASY_MERC_DEATHS",1, 0, 10); - gGameExternalOptions.giExperiencedMercDeaths = iniReader.ReadInteger("JA2 Laptop Settings","EXPERIENCED_MERC_DEATHS",2, 0, 10); - gGameExternalOptions.giExpertMercDeaths = iniReader.ReadInteger("JA2 Laptop Settings","EXPERT_MERC_DEATHS",3, 0, 10); - gGameExternalOptions.giInsaneMercDeaths = iniReader.ReadInteger("JA2 Laptop Settings","INSANE_MERC_DEATHS",4, 0, 10); + gGameExternalOptions.giExperiencedMercDeaths = iniReader.ReadInteger("JA2 Laptop Settings","EXPERIENCED_MERC_DEATHS",2, 0, 10); + gGameExternalOptions.giExpertMercDeaths = iniReader.ReadInteger("JA2 Laptop Settings","EXPERT_MERC_DEATHS",3, 0, 10); + gGameExternalOptions.giInsaneMercDeaths = iniReader.ReadInteger("JA2 Laptop Settings","INSANE_MERC_DEATHS",4, 0, 10); //################# System Settings ################# gGameExternalOptions.gubDeadLockDelay = (UINT8) iniReader.ReadInteger("JA2 System Settings","DEAD_LOCK_DELAY",15, 5, 50); gGameExternalOptions.gfEnableEmergencyButton_SkipStrategicEvents = iniReader.ReadBoolean("JA2 System Settings","ENABLE_EMERGENCY_BUTTON_NUMLOCK_TO_SKIP_STRATEGIC_EVENTS",0); // WDS - Automatically try to save when an assertion failure occurs - gGameExternalOptions.autoSaveOnAssertionFailure = iniReader.ReadBoolean("JA2 System Settings","AUTO_SAVE_ON_ASSERTION_FAILURE", FALSE); - gGameExternalOptions.autoSaveTime = iniReader.ReadInteger("JA2 System Settings","AUTO_SAVE_TIME", 6, 0, 24); + gGameExternalOptions.autoSaveOnAssertionFailure = iniReader.ReadBoolean("JA2 System Settings","AUTO_SAVE_ON_ASSERTION_FAILURE", FALSE); + gGameExternalOptions.autoSaveTime = iniReader.ReadInteger("JA2 System Settings","AUTO_SAVE_TIME", 6, 0, 24); //################# Video Settings ################# gGameExternalOptions.gfVSync = iniReader.ReadBoolean("JA2 Video Settings","VERTICAL_SYNC",0); @@ -617,7 +663,7 @@ void LoadGameExternalOptions() //################# Sound Settings ################# - gGameExternalOptions.guiWeaponSoundEffectsVolume = iniReader.ReadInteger("JA2 Sound Settings","WEAPON_SOUND_EFFECTS_VOLUME",0, 0, 1000 /*1000 = 10x?*/); + gGameExternalOptions.guiWeaponSoundEffectsVolume = iniReader.ReadInteger("JA2 Sound Settings","WEAPON_SOUND_EFFECTS_VOLUME",0, 0, 1000 /*1000 = 10x?*/); @@ -641,14 +687,14 @@ void LoadGameExternalOptions() gGameExternalOptions.gfAllowLimitedVision = iniReader.ReadBoolean("JA2 Tactical Settings","ALLOW_LIMITED_VISION",0); // Maximal search distance for grenades - //gGameExternalOptions.guiMaxTossSearchDist = iniReader.ReadInteger("JA2 Tactical Settings","MaxTossSearchDist",3); + //gGameExternalOptions.guiMaxTossSearchDist = iniReader.ReadInteger("JA2 Tactical Settings","MaxTossSearchDist",3); // Soldier tool tips - removed from here, placed into preferences - SANDRO //gGameExternalOptions.gfAllowSoldierToolTips = iniReader.ReadBoolean("JA2 Tactical Settings","ALLOW_SOLDIER_TOOL_TIPS",0); //SCORE: Settings for UDT - gGameExternalOptions.gfAllowUDTRange = iniReader.ReadBoolean("JA2 Tactical Settings","ALLOW_UDT_RANGE",0); - gGameExternalOptions.gfAllowUDTDetail = iniReader.ReadBoolean("JA2 Tactical Settings","ALLOW_UDT_DETAIL",0); - gGameExternalOptions.ubUDTModifier = (UINT8) iniReader.ReadInteger("JA2 Tactical Settings", "UDT_MODIFIER", 50); + gGameExternalOptions.gfAllowUDTRange = iniReader.ReadBoolean("JA2 Tactical Settings","ALLOW_UDT_RANGE",0); + gGameExternalOptions.gfAllowUDTDetail = iniReader.ReadBoolean("JA2 Tactical Settings","ALLOW_UDT_DETAIL",0); + gGameExternalOptions.ubUDTModifier = (UINT8) iniReader.ReadInteger("JA2 Tactical Settings", "UDT_MODIFIER", 50); // ShadoWarrior: Tooltip changes (start) gGameExternalOptions.ubSoldierTooltipDetailLevel = (UINT8) iniReader.ReadInteger("JA2 Tactical Settings", "SOLDIER_TOOLTIP_DETAIL_LEVEL", 1, 0, 4); @@ -679,25 +725,23 @@ void LoadGameExternalOptions() // ShadoWarrior: Tooltip changes (end) // Unload weapons & remove attachments - gGameExternalOptions.gfShiftFUnloadWeapons = iniReader.ReadBoolean("JA2 Tactical Settings","SHIFT_F_UNLOAD_WEAPONS",1); - gGameExternalOptions.gfShiftFRemoveAttachments = iniReader.ReadBoolean("JA2 Tactical Settings","SHIFT_F_REMOVE_ATTACHMENTS",1); + gGameExternalOptions.gfShiftFUnloadWeapons = iniReader.ReadBoolean("JA2 Tactical Settings","SHIFT_F_UNLOAD_WEAPONS",1); + gGameExternalOptions.gfShiftFRemoveAttachments = iniReader.ReadBoolean("JA2 Tactical Settings","SHIFT_F_REMOVE_ATTACHMENTS",1); gGameExternalOptions.fEnableArmorCoverage = iniReader.ReadBoolean("JA2 Tactical Settings", "ENABLE_ARMOR_COVERAGE", FALSE); // ShadoWarrior for Captain J's armor coverage - // The_Bob - real time sneaking code 01/06/09 // Suport disabling real time sneaking via external .ini file gGameExternalOptions.fAllowRealTimeSneak = iniReader.ReadBoolean("JA2 Tactical Settings","ALLOW_REAL_TIME_SNEAK", FALSE); // Silence the RT sneaking messages gGameExternalOptions.fQuietRealTimeSneak = iniReader.ReadBoolean("JA2 Tactical Settings","QUIET_REAL_TIME_SNEAK", FALSE); - //################# Rain Settings ################## // Rain settings gGameExternalOptions.gfAllowRain = iniReader.ReadBoolean("JA2 Rain Settings","ALLOW_RAIN",0); gGameExternalOptions.gusRainChancePerDay = iniReader.ReadInteger("JA2 Rain Settings","RAIN_CHANCE_PER_DAY",100, 0, 100); - gGameExternalOptions.gusRainMinLength = iniReader.ReadInteger("JA2 Rain Settings","RAIN_MIN_LENGTH_IN_MINUTES",60, 1, 1438 /* 24 hrs - 2 minutes */); - gGameExternalOptions.gusRainMaxLength = iniReader.ReadInteger("JA2 Rain Settings","RAIN_MAX_LENGTH_IN_MINUTES",300, gGameExternalOptions.gusRainMinLength+1, 1439); + gGameExternalOptions.gusRainMinLength = iniReader.ReadInteger("JA2 Rain Settings","RAIN_MIN_LENGTH_IN_MINUTES",60, 1, 1438 /* 24 hrs - 2 minutes */); + gGameExternalOptions.gusRainMaxLength = iniReader.ReadInteger("JA2 Rain Settings","RAIN_MAX_LENGTH_IN_MINUTES",300, gGameExternalOptions.gusRainMinLength+1, 1439); gGameExternalOptions.guiMaxRainDrops = iniReader.ReadInteger("JA2 Rain Settings","MAX_RAIN_DROPS",80, 1, 800); gGameExternalOptions.ubWeaponReliabilityReductionPerRainIntensity = iniReader.ReadInteger("JA2 Rain Settings","WEAPON_RELIABILITY_REDUCTION_PER_RAIN_INTENSITY",0, 0, 10); gGameExternalOptions.ubBreathGainReductionPerRainIntensity = iniReader.ReadDouble("JA2 Rain Settings","BREATH_GAIN_REDUCTION_PER_RAIN_INTENSITY",25, 1, 100); @@ -740,12 +784,12 @@ void LoadGameExternalOptions() gGameExternalOptions.fStealingDisabled = iniReader.ReadBoolean("JA2 Gameplay Settings","STEALING_FROM_SHIPMENTS_DISABLED",FALSE); // WDS: Game progress - gGameExternalOptions.ubGameProgressPortionKills = iniReader.ReadInteger("JA2 Gameplay Settings","GAME_PROGRESS_KILLS", 25, 0, 100); + gGameExternalOptions.ubGameProgressPortionKills = iniReader.ReadInteger("JA2 Gameplay Settings","GAME_PROGRESS_KILLS", 25, 0, 100); gGameExternalOptions.ubGameProgressPortionControl = iniReader.ReadInteger("JA2 Gameplay Settings","GAME_PROGRESS_CONTROL", 25, 0, 100); gGameExternalOptions.ubGameProgressPortionIncome = iniReader.ReadInteger("JA2 Gameplay Settings","GAME_PROGRESS_INCOME", 50, 0, 100); gGameExternalOptions.ubGameProgressPortionVisited = iniReader.ReadInteger("JA2 Gameplay Settings","GAME_PROGRESS_VISITED", 0, 0, 100); - gGameExternalOptions.ubGameProgressMinimum = iniReader.ReadInteger("JA2 Gameplay Settings","GAME_PROGRESS_MINIMUM", 0, 0, 100); - gGameExternalOptions.ubGameProgressIncrement = iniReader.ReadInteger("JA2 Gameplay Settings","GAME_PROGRESS_INCREMENT", 0, 0, 100); + gGameExternalOptions.ubGameProgressMinimum = iniReader.ReadInteger("JA2 Gameplay Settings","GAME_PROGRESS_MINIMUM", 0, 0, 100); + gGameExternalOptions.ubGameProgressIncrement = iniReader.ReadInteger("JA2 Gameplay Settings","GAME_PROGRESS_INCREMENT", 0, 0, 100); // Any way to warn on this? if (gGameExternalOptions.ubGameProgressPortionKills + @@ -753,7 +797,7 @@ void LoadGameExternalOptions() gGameExternalOptions.ubGameProgressPortionIncome + gGameExternalOptions.ubGameProgressPortionVisited != 100) { - gGameExternalOptions.ubGameProgressPortionKills = 25; + gGameExternalOptions.ubGameProgressPortionKills = 25; gGameExternalOptions.ubGameProgressPortionControl = 25; gGameExternalOptions.ubGameProgressPortionIncome = 50; gGameExternalOptions.ubGameProgressPortionVisited = 0; @@ -763,12 +807,12 @@ void LoadGameExternalOptions() //gGameExternalOptions.ubIMPStartingLevel = iniReader.ReadInteger("JA2 Gameplay Settings","IMP_STARTING_LEVEL", 1, 1, 7); // WDS - make number of mercenaries, etc. be configurable - gGameExternalOptions.ubGameMaximumNumberOfPlayerMercs = iniReader.ReadInteger("JA2 Gameplay Settings","MAX_NUMBER_PLAYER_MERCS", 24, 1, CODE_MAXIMUM_NUMBER_OF_PLAYER_MERCS); - gGameExternalOptions.ubGameMaximumNumberOfPlayerVehicles = iniReader.ReadInteger("JA2 Gameplay Settings","MAX_NUMBER_PLAYER_VEHICLES", 2, 2, CODE_MAXIMUM_NUMBER_OF_PLAYER_VEHICLES); - gGameExternalOptions.ubGameMaximumNumberOfEnemies = iniReader.ReadInteger("JA2 Gameplay Settings","MAX_NUMBER_ENEMIES", 32, 16, CODE_MAXIMUM_NUMBER_OF_ENEMIES); - gGameExternalOptions.ubGameMaximumNumberOfCreatures = iniReader.ReadInteger("JA2 Gameplay Settings","MAX_NUMBER_CREATURES", 32, 0, CODE_MAXIMUM_NUMBER_OF_CREATURES); - gGameExternalOptions.ubGameMaximumNumberOfRebels = iniReader.ReadInteger("JA2 Gameplay Settings","MAX_NUMBER_REBELS", 32, 16, CODE_MAXIMUM_NUMBER_OF_REBELS); - gGameExternalOptions.ubGameMaximumNumberOfCivilians = iniReader.ReadInteger("JA2 Gameplay Settings","MAX_NUMBER_CIVS", 32, 16, CODE_MAXIMUM_NUMBER_OF_CIVS); + gGameExternalOptions.ubGameMaximumNumberOfPlayerMercs = iniReader.ReadInteger("JA2 Gameplay Settings","MAX_NUMBER_PLAYER_MERCS", 24, 1, CODE_MAXIMUM_NUMBER_OF_PLAYER_MERCS); + gGameExternalOptions.ubGameMaximumNumberOfPlayerVehicles = iniReader.ReadInteger("JA2 Gameplay Settings","MAX_NUMBER_PLAYER_VEHICLES", 2, 2, CODE_MAXIMUM_NUMBER_OF_PLAYER_VEHICLES); + gGameExternalOptions.ubGameMaximumNumberOfEnemies = iniReader.ReadInteger("JA2 Gameplay Settings","MAX_NUMBER_ENEMIES", 32, 16, CODE_MAXIMUM_NUMBER_OF_ENEMIES); + gGameExternalOptions.ubGameMaximumNumberOfCreatures = iniReader.ReadInteger("JA2 Gameplay Settings","MAX_NUMBER_CREATURES", 32, 0, CODE_MAXIMUM_NUMBER_OF_CREATURES); + gGameExternalOptions.ubGameMaximumNumberOfRebels = iniReader.ReadInteger("JA2 Gameplay Settings","MAX_NUMBER_REBELS", 32, 16, CODE_MAXIMUM_NUMBER_OF_REBELS); + gGameExternalOptions.ubGameMaximumNumberOfCivilians = iniReader.ReadInteger("JA2 Gameplay Settings","MAX_NUMBER_CIVS", 32, 16, CODE_MAXIMUM_NUMBER_OF_CIVS); //Global game events gGameExternalOptions.ubGameProgressStartMadlabQuest = iniReader.ReadInteger("JA2 Gameplay Settings","GAME_PROGRESS_START_MADLAB_QUEST",35, 1, 100); @@ -851,7 +895,7 @@ void LoadGameExternalOptions() gGameExternalOptions.gfInsaneAggressiveQueen = iniReader.ReadBoolean("JA2 Gameplay Settings","INSANE_QUEEN_AGGRESSIVE",TRUE); // WDS - New AI - gGameExternalOptions.useNewAI = iniReader.ReadBoolean("JA2 Gameplay Settings","NEW_AGGRESSIVE_AI",FALSE); + gGameExternalOptions.useNewAI = iniReader.ReadBoolean("JA2 Gameplay Settings","NEW_AGGRESSIVE_AI",FALSE); gGameExternalOptions.gfInvestigateSector = iniReader.ReadBoolean("JA2 Gameplay Settings","ENEMY_INVESTIGATE_SECTOR",FALSE); gGameExternalOptions.gfReassignPendingReinforcements = iniReader.ReadBoolean("JA2 Gameplay Settings","REASSIGN_PENDING_REINFORCEMENTS",TRUE); @@ -886,14 +930,14 @@ void LoadGameExternalOptions() gGameExternalOptions.gflimitedRoaming = iniReader.ReadBoolean("JA2 Gameplay Settings","RESTRICT_ROAMING",FALSE); // WDS - Improve Tony's and Devin's inventory like BR's - gGameExternalOptions.tonyUsesBRSetting = iniReader.ReadBoolean("JA2 Gameplay Settings","TONY_USES_BR_SETTING", FALSE); - gGameExternalOptions.devinUsesBRSetting = iniReader.ReadBoolean("JA2 Gameplay Settings","DEVIN_USES_BR_SETTING", FALSE); + gGameExternalOptions.tonyUsesBRSetting = iniReader.ReadBoolean("JA2 Gameplay Settings","TONY_USES_BR_SETTING", FALSE); + gGameExternalOptions.devinUsesBRSetting = iniReader.ReadBoolean("JA2 Gameplay Settings","DEVIN_USES_BR_SETTING", FALSE); // WDS - Smart goggle switching - gGameExternalOptions.smartGoggleSwitch = iniReader.ReadBoolean("JA2 Gameplay Settings","SMART_GOGGLE_SWITCH", FALSE); + gGameExternalOptions.smartGoggleSwitch = iniReader.ReadBoolean("JA2 Gameplay Settings","SMART_GOGGLE_SWITCH", FALSE); // WDS - Automatically flag mines - gGameExternalOptions.automaticallyFlagMines = iniReader.ReadBoolean("JA2 Gameplay Settings","AUTOMATICALLY_FLAG_MINES", FALSE); + gGameExternalOptions.automaticallyFlagMines = iniReader.ReadBoolean("JA2 Gameplay Settings","AUTOMATICALLY_FLAG_MINES", FALSE); //Assignment Settings (training, repairing, doctoring, etc) gGameExternalOptions.ubAssignmentUnitsPerDay = iniReader.ReadInteger("JA2 Gameplay Settings","ASSIGNMENT_UNITS_PER_DAY",24, 1, 96); @@ -916,8 +960,8 @@ void LoadGameExternalOptions() gGameExternalOptions.ubHighActivityLevel = iniReader.ReadInteger("JA2 Gameplay Settings","HIGH_ACTIVITY_LEVEL",12, 1, 100); gGameExternalOptions.ubDoctoringRateDivisor = iniReader.ReadInteger("JA2 Gameplay Settings","DOCTORING_RATE_DIVISOR",2400, 1200, 24000); gGameExternalOptions.ubHospitalHealingRate = iniReader.ReadInteger("JA2 Gameplay Settings","HOSPITAL_HEALING_RATE",5, 1, 10); - gGameExternalOptions.ubBaseMedicalSkillToDealWithEmergency = iniReader.ReadInteger("JA2 Gameplay Settings","BASE_MEDICAL_SKILL_TO_DEAL_WITH_EMERGENCY",20, 0, 100); - gGameExternalOptions.ubMultiplierForDifferenceInLifeValueForEmergency = iniReader.ReadInteger("JA2 Gameplay Settings","MULTIPLIER_FOR_DIFFERENCE_IN_LIFE_VALUE_FOR_EMERGENCY",4, 1, 10); + gGameExternalOptions.ubBaseMedicalSkillToDealWithEmergency = iniReader.ReadInteger("JA2 Gameplay Settings","BASE_MEDICAL_SKILL_TO_DEAL_WITH_EMERGENCY",20, 0, 100); + gGameExternalOptions.ubMultiplierForDifferenceInLifeValueForEmergency = iniReader.ReadInteger("JA2 Gameplay Settings","MULTIPLIER_FOR_DIFFERENCE_IN_LIFE_VALUE_FOR_EMERGENCY",4, 1, 10); gGameExternalOptions.ubPointCostPerHealthBelowOkLife = iniReader.ReadInteger("JA2 Gameplay Settings","POINT_COST_PER_HEALTH_BELOW_OKLIFE",2, 1, 5);//OKLIFE = 15 gGameExternalOptions.ubRepairCostPerJam = iniReader.ReadInteger("JA2 Gameplay Settings","REPAIR_COST_PER_JAM",2, 1, 10); @@ -943,116 +987,116 @@ void LoadGameExternalOptions() gGameExternalOptions.fEnableSlayForever = iniReader.ReadBoolean("JA2 Gameplay Settings", "SLAY_FOREVER", FALSE); // CHRISL: Setting to turn off the description and stack popup options from the sector inventory panel - gGameExternalOptions.fSectorDesc = iniReader.ReadBoolean("JA2 Gameplay Settings","ALLOW_SECTOR_DESCRIPTION_WINDOW",TRUE); + gGameExternalOptions.fSectorDesc = iniReader.ReadBoolean("JA2 Gameplay Settings","ALLOW_SECTOR_DESCRIPTION_WINDOW",TRUE); //afp - use bullet tracers? - gGameExternalOptions.gbBulletTracer = iniReader.ReadBoolean("JA2 Gameplay Settings","USE_BULLET_TRACERS",FALSE); + gGameExternalOptions.gbBulletTracer = iniReader.ReadBoolean("JA2 Gameplay Settings","USE_BULLET_TRACERS",FALSE); - gGameExternalOptions.fRestrictFemaleEnemiesExceptElite = iniReader.ReadBoolean("JA2 Gameplay Settings","RESTRICT_FEMALE_ENEMIES_EXCEPT_ELITE",FALSE); + gGameExternalOptions.fRestrictFemaleEnemiesExceptElite = iniReader.ReadBoolean("JA2 Gameplay Settings","RESTRICT_FEMALE_ENEMIES_EXCEPT_ELITE",FALSE); // HEADROCK: Use Enhanced Item Description Box? - gGameExternalOptions.fEnhancedDescriptionBox = iniReader.ReadInteger("JA2 Gameplay Settings","USE_ENHANCED_DESCRIPTION_BOX",0, 0, 2); + gGameExternalOptions.fEnhancedDescriptionBox = iniReader.ReadInteger("JA2 Gameplay Settings","USE_ENHANCED_DESCRIPTION_BOX",0, 0, 2); // WANNE: Always use prof.dat?? - gGameExternalOptions.fAlwaysUseProfDat = iniReader.ReadBoolean("JA2 Gameplay Settings", "ALWAYS_USE_PROF_DAT", FALSE); + gGameExternalOptions.fAlwaysUseProfDat = iniReader.ReadBoolean("JA2 Gameplay Settings", "ALWAYS_USE_PROF_DAT", FALSE); // HEADROCK HAM B1: Set % of mine income, where 100% = normal - gGameExternalOptions.iMineIncomePercentage = iniReader.ReadInteger("JA2 HAM Settings","MINE_INCOME_PERCENTAGE",0); + gGameExternalOptions.iMineIncomePercentage = iniReader.ReadInteger("JA2 HAM Settings","MINE_INCOME_PERCENTAGE",0); // HEADROCK HAM B2.4: Set a threshold at 1% - otherwise game crashes on asserting mine income. // Can anyone have a look at circumventing the income check? I don't want to cause any damage... gGameExternalOptions.iMineIncomePercentage = __max(gGameExternalOptions.iMineIncomePercentage, 100); // HEADROCK HAM B1: Set minimum and maximum CTH - gGameExternalOptions.iMaximumCTH = iniReader.ReadInteger("JA2 HAM Settings","MAXIMUM_POSSIBLE_CTH",99); + gGameExternalOptions.iMaximumCTH = iniReader.ReadInteger("JA2 HAM Settings","MAXIMUM_POSSIBLE_CTH",99); - gGameExternalOptions.iMinimumCTH = iniReader.ReadInteger("JA2 HAM Settings","MINIMUM_POSSIBLE_CTH",0); + gGameExternalOptions.iMinimumCTH = iniReader.ReadInteger("JA2 HAM Settings","MINIMUM_POSSIBLE_CTH",0); // HEADROCK HAM B1: Set minimum CTH at fraction between 0 and 1 ( MINCTH = 1/(100*divisor) ) Note Minimum above must be 0. - gGameExternalOptions.iMinimumCTHDivisor = iniReader.ReadInteger("JA2 HAM Settings","MINIMUM_CTH_DIVISOR",100); + gGameExternalOptions.iMinimumCTHDivisor = iniReader.ReadInteger("JA2 HAM Settings","MINIMUM_CTH_DIVISOR",100); // HEADROCK HAM B1: Allow restricted militia to move through visited sectors? - gGameExternalOptions.bUnrestrictVisited = iniReader.ReadBoolean("JA2 HAM Settings","ALLOW_RESTRICTED_MILITIA_THROUGH_VISITED_SECTORS",FALSE); + gGameExternalOptions.bUnrestrictVisited = iniReader.ReadBoolean("JA2 HAM Settings","ALLOW_RESTRICTED_MILITIA_THROUGH_VISITED_SECTORS",FALSE); - gGameExternalOptions.bDynamicRestrictRoaming = iniReader.ReadBoolean("JA2 HAM Settings","ALLOW_DYNAMIC_RESTRICTED_ROAMING",TRUE); + gGameExternalOptions.bDynamicRestrictRoaming = iniReader.ReadBoolean("JA2 HAM Settings","ALLOW_DYNAMIC_RESTRICTED_ROAMING",TRUE); // HEADROCK HAM B2: Reset suppression counter. 0 = never (oldskool), 1 = Every turn, 2 = Every attack. - gGameExternalOptions.iClearSuppression = iniReader.ReadInteger("JA2 HAM Settings","CLEAR_SUPPRESSION_COUNTER",1); + gGameExternalOptions.iClearSuppression = iniReader.ReadInteger("JA2 HAM Settings","CLEAR_SUPPRESSION_COUNTER",1); // HEADROCK HAM B2.1: This controls how effective suppression is, by increasing the number of ubSuppressionPoints accumulated by combatants (percentage); - gGameExternalOptions.iSuppressionEffectiveness = iniReader.ReadInteger("JA2 HAM Settings","SUPPRESSION_EFFECTIVENESS",0); + gGameExternalOptions.iSuppressionEffectiveness = iniReader.ReadInteger("JA2 HAM Settings","SUPPRESSION_EFFECTIVENESS",0); // HEADROCK HAM B2: MAXIMUM number of APs that can be lost to suppression in a given turn (0=unlimited) - gGameExternalOptions.fSuppressionAPLossPerTurn = iniReader.ReadBoolean("JA2 HAM Settings","LIMITED_SUPPRESSION_AP_LOSS_PER_TURN",TRUE); + gGameExternalOptions.fSuppressionAPLossPerTurn = iniReader.ReadBoolean("JA2 HAM Settings","LIMITED_SUPPRESSION_AP_LOSS_PER_TURN",TRUE); // HEADROCK HAM B2: MAXIMUM number of APs that can be lost to suppression in a given attack (0=unlimited) - gGameExternalOptions.fSuppressionAPLossPerAttack = iniReader.ReadBoolean("JA2 HAM Settings","LIMITED_SUPPRESSION_AP_LOSS_PER_ATTACK",TRUE); + gGameExternalOptions.fSuppressionAPLossPerAttack = iniReader.ReadBoolean("JA2 HAM Settings","LIMITED_SUPPRESSION_AP_LOSS_PER_ATTACK",TRUE); gGameExternalOptions.iSuppressionToleranceMax = iniReader.ReadInteger("JA2 HAM Settings","SUPPRESSION_TOLERANCE_MAX", 18, 1, 24); gGameExternalOptions.iSuppressionToleranceMin = iniReader.ReadInteger("JA2 HAM Settings","SUPPRESSION_TOLERANCE_MIN", 1, 0, 24); // HEADROCK HAM B2: Suppression Shock ON/OFF - gGameExternalOptions.fSuppressionShock = iniReader.ReadBoolean("JA2 HAM Settings","SUPPRESSION_SHOCK",FALSE); + gGameExternalOptions.fSuppressionShock = iniReader.ReadBoolean("JA2 HAM Settings","SUPPRESSION_SHOCK",FALSE); // HEADROCK HAM B2: Suppression Shock effectiveness (percentage, 100 = "normal", 0 = deactivated. Range 0-65535) - gGameExternalOptions.iSuppressionShockEffectiveness = iniReader.ReadInteger("JA2 HAM Settings","SUPPRESSION_SHOCK_EFFECTIVENESS",0); + gGameExternalOptions.iSuppressionShockEffectiveness = iniReader.ReadInteger("JA2 HAM Settings","SUPPRESSION_SHOCK_EFFECTIVENESS",0); // HEADROCK HAM B2.1: CTH penalty given by a "Cowering" target to any enemy shooter. - gGameExternalOptions.iAimPenaltyPerTargetShock = iniReader.ReadInteger("JA2 HAM Settings","AIM_PENALTY_PER_TARGET_SHOCK",0); + gGameExternalOptions.iAimPenaltyPerTargetShock = iniReader.ReadInteger("JA2 HAM Settings","AIM_PENALTY_PER_TARGET_SHOCK",0); // HEADROCK HAM B2.3: A "cowering" soldier is twice as susceptible to suppression. - gGameExternalOptions.iCowerEffectOnSuppression = iniReader.ReadInteger("JA2 HAM Settings","COWER_EFFECT_ON_SUPPRESSION",0); + gGameExternalOptions.iCowerEffectOnSuppression = iniReader.ReadInteger("JA2 HAM Settings","COWER_EFFECT_ON_SUPPRESSION",0); // HEADROCK HAM B2.5: Turn on Realistic Tracers. 0 = off (regular tracers). 1 = Fully realistic tracers. 2 = Tracer Bump + 1.13 Autofire Penalty Reduction - gGameExternalOptions.iRealisticTracers = iniReader.ReadInteger("JA2 HAM Settings","REALISTIC_TRACERS",0); + gGameExternalOptions.iRealisticTracers = iniReader.ReadInteger("JA2 HAM Settings","REALISTIC_TRACERS",0); // HEADROCK HAM B2.5: Realistic tracers - one of every X bullets in a tracer magazines will be a tracer bullet. 0 = off (JA2 normal) - gGameExternalOptions.iNumBulletsPerTracer = __max(iniReader.ReadInteger("JA2 HAM Settings","NUM_BULLETS_PER_TRACER",1),1); + gGameExternalOptions.iNumBulletsPerTracer = __max(iniReader.ReadInteger("JA2 HAM Settings","NUM_BULLETS_PER_TRACER",1),1); // HEADROCK HAM B2.5: Realistic tracers - CTH increased by this amount whenever a tracer is fired. 0 = off. - gGameExternalOptions.iCTHBumpPerTracer = iniReader.ReadInteger("JA2 HAM Settings","CTH_BUMP_PER_TRACER",0); + gGameExternalOptions.iCTHBumpPerTracer = iniReader.ReadInteger("JA2 HAM Settings","CTH_BUMP_PER_TRACER",0); // HEADROCK HAM B2.6: Increased aiming costs? - gGameExternalOptions.fIncreasedAimingCost = iniReader.ReadBoolean("JA2 HAM Settings","INCREASED_AIM_COST", FALSE); + gGameExternalOptions.fIncreasedAimingCost = iniReader.ReadBoolean("JA2 HAM Settings","INCREASED_AIM_COST", FALSE); // HEADROCK HAM B2.6: Dynamically determine Max-Aiming based also on weapon, bipod, etc? - gGameExternalOptions.fDynamicAimingTime = iniReader.ReadBoolean("JA2 HAM Settings","DYNAMIC_AIM_LIMITS", FALSE); + gGameExternalOptions.fDynamicAimingTime = iniReader.ReadBoolean("JA2 HAM Settings","DYNAMIC_AIM_LIMITS", FALSE); // HEADROCK HAM B2.6: Controls how much effect target movement has on aiming - //gGameExternalOptions.iMovementEffectOnAiming = iniReader.ReadFloat("JA2 HAM Settings","MOVEMENT_EFFECT_ON_AIMING", 1.5); - gGameExternalOptions.iMovementEffectOnAiming = (float)iniReader.ReadDouble("JA2 HAM Settings","MOVEMENT_EFFECT_ON_AIMING", 1.5, 0.0, 255.0); + //gGameExternalOptions.iMovementEffectOnAiming = iniReader.ReadFloat("JA2 HAM Settings","MOVEMENT_EFFECT_ON_AIMING", 1.5); + gGameExternalOptions.iMovementEffectOnAiming = (float)iniReader.ReadDouble("JA2 HAM Settings","MOVEMENT_EFFECT_ON_AIMING", 1.5, 0.0, 255.0); // HEADROCK HAM B2.6: Autofire Bullets/5AP modifier gGameExternalOptions.iAutofireBulletsPer5APModifier = iniReader.ReadInteger("JA2 HAM Settings","AUTOFIRE_BULLETS_PER_5AP_MODIFIER", 0); // HEADROCK HAM B2.6/B1: Adjustable "luck" factor in Auto-Resolve //gGameExternalOptions.iAutoResolveLuckFactor = (float)(__max(iniReader.ReadFloat("JA2 HAM Settings","AUTORESOLVE_LUCK_FACTOR", 2.0),1.0)); - gGameExternalOptions.iAutoResolveLuckFactor = (float)(__max(iniReader.ReadDouble("JA2 HAM Settings","AUTORESOLVE_LUCK_FACTOR", 2.0, 0.0, 255.0),1.0)); + gGameExternalOptions.iAutoResolveLuckFactor = (float)(__max(iniReader.ReadDouble("JA2 HAM Settings","AUTORESOLVE_LUCK_FACTOR", 2.0, 0.0, 255.0),1.0)); // HEADROCK HAM B2.6: Adjustable maximum for Suppression Shock effect. This has the added effect of reducing overall susceptibility to shock and may render some enemies unshockable. - gGameExternalOptions.iMaxSuppressionShock = iniReader.ReadInteger("JA2 HAM Settings","MAXIMUM_SUPPRESSION_SHOCK", 0); + gGameExternalOptions.iMaxSuppressionShock = iniReader.ReadInteger("JA2 HAM Settings","MAXIMUM_SUPPRESSION_SHOCK", 0); // HEADROCK HAM B2.6/2/1: Toggle new Burst/Auto CTH bars: 0=neither, 1=both, 2=Burst, 3=Auto - gGameExternalOptions.iNewCTHBars = iniReader.ReadInteger("JA2 HAM Settings","NEW_BURST-AUTO_CTH_BARS", 0); + gGameExternalOptions.iNewCTHBars = iniReader.ReadInteger("JA2 HAM Settings","NEW_BURST-AUTO_CTH_BARS", 0); // HEADROCK HAM B2.6: Toggle whether AI checks for larger magazine when wanting to suppress at a distance - gGameExternalOptions.fIncreaseAISuppressionFire = iniReader.ReadBoolean("JA2 HAM Settings","INCREASE_AI_WILLINGNESS_TO_SUPPRESS", FALSE); + gGameExternalOptions.fIncreaseAISuppressionFire = iniReader.ReadBoolean("JA2 HAM Settings","INCREASE_AI_WILLINGNESS_TO_SUPPRESS", FALSE); // HEADROCK HAM B2.7: Change the speed of skill progression. (defaults set to JA2 normal) - gGameExternalOptions.ubHealthSubpointsToImprove = __max(1,iniReader.ReadInteger("JA2 HAM Settings","HEALTH_SUBPOINTS_TO_IMPROVE", 50)); + gGameExternalOptions.ubHealthSubpointsToImprove = __max(1,iniReader.ReadInteger("JA2 HAM Settings","HEALTH_SUBPOINTS_TO_IMPROVE", 50)); gGameExternalOptions.ubStrengthSubpointsToImprove = __max(1,iniReader.ReadInteger("JA2 HAM Settings","STRENGTH_SUBPOINTS_TO_IMPROVE", 50)); gGameExternalOptions.ubDexteritySubpointsToImprove = __max(1,iniReader.ReadInteger("JA2 HAM Settings","DEXTERITY_SUBPOINTS_TO_IMPROVE", 50)); gGameExternalOptions.ubAgilitySubpointsToImprove = __max(1,iniReader.ReadInteger("JA2 HAM Settings","AGILITY_SUBPOINTS_TO_IMPROVE", 50)); - gGameExternalOptions.ubWisdomSubpointsToImprove = __max(1,iniReader.ReadInteger("JA2 HAM Settings","WISDOM_SUBPOINTS_TO_IMPROVE", 50)); - gGameExternalOptions.ubMarksmanshipSubpointsToImprove = __max(1,iniReader.ReadInteger("JA2 HAM Settings","MARKSMANSHIP_SUBPOINTS_TO_IMPROVE", 25)); + gGameExternalOptions.ubWisdomSubpointsToImprove = __max(1,iniReader.ReadInteger("JA2 HAM Settings","WISDOM_SUBPOINTS_TO_IMPROVE", 50)); + gGameExternalOptions.ubMarksmanshipSubpointsToImprove = __max(1,iniReader.ReadInteger("JA2 HAM Settings","MARKSMANSHIP_SUBPOINTS_TO_IMPROVE", 25)); gGameExternalOptions.ubMedicalSubpointsToImprove = __max(1,iniReader.ReadInteger("JA2 HAM Settings","MEDICAL_SUBPOINTS_TO_IMPROVE", 25)); gGameExternalOptions.ubMechanicalSubpointsToImprove = __max(1,iniReader.ReadInteger("JA2 HAM Settings","MECHANICAL_SUBPOINTS_TO_IMPROVE", 25)); gGameExternalOptions.ubExplosivesSubpointsToImprove = __max(1,iniReader.ReadInteger("JA2 HAM Settings","EXPLOSIVES_SUBPOINTS_TO_IMPROVE", 25)); gGameExternalOptions.ubLeadershipSubpointsToImprove = __max(1,iniReader.ReadInteger("JA2 HAM Settings","LEADERSHIP_SUBPOINTS_TO_IMPROVE", 25)); - gGameExternalOptions.ubLevelSubpointsToImprove = __max(1,__min(iniReader.ReadInteger("JA2 HAM Settings","LEVEL_SUBPOINTS_TO_IMPROVE", 350), 6500)); + gGameExternalOptions.ubLevelSubpointsToImprove = __max(1,__min(iniReader.ReadInteger("JA2 HAM Settings","LEVEL_SUBPOINTS_TO_IMPROVE", 350), 6500)); // HEADROCK HAM B2.7: When turned on, this will give a CTH approximation instead of an exact value, on CTH Bars and "F" key feedback. - gGameExternalOptions.fApproximateCTH = iniReader.ReadBoolean("JA2 HAM Settings","APPROXIMATE_CTH", FALSE); + gGameExternalOptions.fApproximateCTH = iniReader.ReadBoolean("JA2 HAM Settings","APPROXIMATE_CTH", FALSE); // HEADROCK HAM B2.7: Augmented Roaming Militia code - turn this to TRUE to allow militia free travel through San Mona, Tixa, Orta, Omerta, and Estoni. gGameExternalOptions.fAllowMilitiaMoveThroughMinorCities = iniReader.ReadBoolean("JA2 HAM Settings","ALLOW_MILITIA_MOVE_THROUGH_MINOR_CITIES", FALSE); @@ -1064,32 +1108,32 @@ void LoadGameExternalOptions() gGameExternalOptions.fDiverseRoamingMilitiaGroups = iniReader.ReadBoolean("JA2 HAM Settings","DIVERSE_ROAMING_MILITIA_GROUPS", FALSE); // HEADROCK HAM B2.8: This setting controls whether militia will try to join up into the largest groups they can, or average out the size of the groups to cover more territory. - gGameExternalOptions.ubRoamingMilitiaSpreadsOutChance = iniReader.ReadInteger("JA2 HAM Settings","ROAMING_MILITIA_SPREADOUT_CHANCE", 0); + gGameExternalOptions.ubRoamingMilitiaSpreadsOutChance = iniReader.ReadInteger("JA2 HAM Settings","ROAMING_MILITIA_SPREADOUT_CHANCE", 0); // HEADROCK HAM B2.8: These are new cowering penalty divisors that help us determine how effective cowering is in different stances and when the shooter is targetting different bodyparts - gGameExternalOptions.ubCoweringPenaltyDivisorProne = __max(1,iniReader.ReadInteger("JA2 HAM Settings","CTH_PENALTY_FOR_COWERING_PRONE_TARGET_DIVISOR", 1)); + gGameExternalOptions.ubCoweringPenaltyDivisorProne = __max(1,iniReader.ReadInteger("JA2 HAM Settings","CTH_PENALTY_FOR_COWERING_PRONE_TARGET_DIVISOR", 1)); gGameExternalOptions.ubCoweringPenaltyDivisorCrouchedHead = __max(1,iniReader.ReadInteger("JA2 HAM Settings","CTH_PENALTY_FOR_COWERING_CROUCHED_TARGET_HEAD_DIVISOR", 3)); gGameExternalOptions.ubCoweringPenaltyDivisorCrouchedTorso = __max(1,iniReader.ReadInteger("JA2 HAM Settings","CTH_PENALTY_FOR_COWERING_CROUCHED_TARGET_TORSO_DIVISOR", 4)); gGameExternalOptions.ubCoweringPenaltyDivisorCrouchedLegs = __max(1,iniReader.ReadInteger("JA2 HAM Settings","CTH_PENALTY_FOR_COWERING_CROUCHED_TARGET_LEGS_DIVISOR", 5)); // HEADROCK HAM B2.8: This is the maximum range at which a target gives out the full CTH penalty for cowering. At lower range, it'll give proportionally less penalty. - gGameExternalOptions.usMinRangeForFullCoweringPenalty = __max(10,iniReader.ReadInteger("JA2 HAM Settings","MIN_RANGE_FOR_FULL_COWERING_TARGET_PENALTY", 300)); + gGameExternalOptions.usMinRangeForFullCoweringPenalty = __max(10,iniReader.ReadInteger("JA2 HAM Settings","MIN_RANGE_FOR_FULL_COWERING_TARGET_PENALTY", 300)); // HEADROCK HAM B2.8: Absolute maximum CTH penalty from target/shooter cowering gGameExternalOptions.usMaxShooterCoweringPenalty = __max(1,iniReader.ReadInteger("JA2 HAM Settings","MAX_SHOOTER_COWERING_PENALTY", 0)); - gGameExternalOptions.usMaxTargetCoweringPenalty = __max(1,iniReader.ReadInteger("JA2 HAM Settings","MAX_TARGET_COWERING_PENALTY", 0)); + gGameExternalOptions.usMaxTargetCoweringPenalty = __max(1,iniReader.ReadInteger("JA2 HAM Settings","MAX_TARGET_COWERING_PENALTY", 0)); // HEADROCK HAM B2.8: If this is turned on, Militia will drop their equipment similar to enemies, IF killed by non-player character. - gGameExternalOptions.ubMilitiaDropEquipment = iniReader.ReadInteger("JA2 HAM Settings","MILITIA_DROP_EQUIPMENT", 0); + gGameExternalOptions.ubMilitiaDropEquipment = iniReader.ReadInteger("JA2 HAM Settings","MILITIA_DROP_EQUIPMENT", 0); // HEADROCK HAM B2.8: New Trainer Relations: 2 = Trainees will go to sleep when their trainer goes to sleep. 3 = Trainer will go to sleep if all trainees are asleep. 1 = Both. 0 = Neither. - gGameExternalOptions.ubSmartTrainingSleep = iniReader.ReadInteger("JA2 HAM Settings","SMART_TRAINING-SLEEP_HANDLER", 0); + gGameExternalOptions.ubSmartTrainingSleep = iniReader.ReadInteger("JA2 HAM Settings","SMART_TRAINING-SLEEP_HANDLER", 0); // HEADROCK HAM B2.8: New Trainer Relations: 2 = Trainees will wake up when their trainer wakes up. 3 = Trainer will wake up if all trainees wake up. 1 = Both. 0 = Neither. - gGameExternalOptions.ubSmartTrainingWake = iniReader.ReadInteger("JA2 HAM Settings","SMART_TRAINING-WAKE_HANDLER", 0); + gGameExternalOptions.ubSmartTrainingWake = iniReader.ReadInteger("JA2 HAM Settings","SMART_TRAINING-WAKE_HANDLER", 0); // HEADROCK HAM B2.8: New Trainer Relations: 2 = Trainers will rest if no trainees available. 3 = Trainees will rest if no trainers available (not recommended). 1 = Both. 0 = Neither. - gGameExternalOptions.ubSmartTrainingRest = iniReader.ReadInteger("JA2 HAM Settings","SMART_TRAINING_REST", 0); + gGameExternalOptions.ubSmartTrainingRest = iniReader.ReadInteger("JA2 HAM Settings","SMART_TRAINING_REST", 0); } INT16 DynamicAdjustAPConstants(INT16 iniReadValue, INT16 iniDefaultValue, BOOLEAN reverse) diff --git a/GameSettings.h b/GameSettings.h index 59d129f6..5cccd1a4 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -189,7 +189,6 @@ typedef struct INT32 iIMPStartingLevelCostMultiplier; INT32 iBonusPointsForDisability; INT32 iBonusPointsPerSkillNotTaken; - INT32 iMaxMilitiaPerSector; INT32 iTrainingSquadSize; INT32 iMilitiaTrainingCost; diff --git a/GameVersion.cpp b/GameVersion.cpp index fcb1ad21..57b345a9 100644 --- a/GameVersion.cpp +++ b/GameVersion.cpp @@ -13,12 +13,12 @@ #ifdef JA2EDITOR //MAP EDITOR BUILD VERSION -CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.2949" }; +CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.2918" }; #elif defined JA2BETAVERSION //BETA/TEST BUILD VERSION -CHAR16 zVersionLabel[256] = { L"Debug v1.13.2949" }; +CHAR16 zVersionLabel[256] = { L"Debug v1.13.2918" }; #elif defined CRIPPLED_VERSION @@ -28,11 +28,11 @@ CHAR16 zVersionLabel[256] = { L"Beta v. 0.98" }; #else //RELEASE BUILD VERSION - CHAR16 zVersionLabel[256] = { L"Release v1.13.2949" }; + CHAR16 zVersionLabel[256] = { L"Multiplayer v1.1" }; #endif -CHAR8 czVersionNumber[16] = { "Build 09.06.04" }; //YY.MM.DD +CHAR8 czVersionNumber[16] = { "Build 09.06.01" }; //YY.MM.DD CHAR16 zTrackingNumber[16] = { L"Z" }; diff --git a/Init.cpp b/Init.cpp index 9676cf6d..916f4605 100644 --- a/Init.cpp +++ b/Init.cpp @@ -67,6 +67,9 @@ extern INT16 APBPConstants[TOTAL_APBP_VALUES] = {0}; extern INT16 gubMaxActionPoints[28];//MAXBODYTYPES = 28... JUST GETTING IT TO WORK NOW. GOTTHARD 7/2/08 extern BOOLEAN GetCDromDriveLetter( STR8 pString ); +#include "PostalService.h" +extern CPostalService gPostalService; + // The InitializeGame function is responsible for setting up all data and Gaming Engine // tasks which will run the game @@ -119,6 +122,33 @@ static void AddLanguagePrefix(STR fileName, const STR language) memmove( fileComponent, language, strlen( language) ); } +static void AddLanguagePrefix(STR fileName) +{ +#ifdef GERMAN + AddLanguagePrefix( fileName, GERMAN_PREFIX); +#endif +#ifdef RUSSIAN + AddLanguagePrefix( fileName, RUSSIAN_PREFIX); +#endif +#ifdef DUTCH + AddLanguagePrefix( fileName, DUTCH_PREFIX); +#endif +#ifdef POLISH + AddLanguagePrefix( fileName, POLISH_PREFIX); +#endif +#ifdef FRENCH + AddLanguagePrefix( fileName, FRENCH_PREFIX); +#endif +#ifdef ITALIAN + AddLanguagePrefix( fileName, ITALIAN_PREFIX); +#endif +#ifdef TAIWANESE + AddLanguagePrefix( fileName, TAIWANESE_PREFIX); +#endif +#ifdef CHINESE + AddLanguagePrefix( fileName, CHINESE_PREFIX); +#endif +} BOOLEAN LoadExternalGameplayData(STR directoryName) { @@ -128,32 +158,27 @@ BOOLEAN LoadExternalGameplayData(STR directoryName) strcpy(fileName, directoryName); strcat(fileName, ENEMYMISCDROPSFILENAME); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); - if(!ReadInEnemyMiscDropsStats(gEnemyMiscDrops, fileName)) - return FALSE; + THROWIFFALSE(ReadInEnemyMiscDropsStats(gEnemyMiscDrops, fileName), ENEMYMISCDROPSFILENAME); strcpy(fileName, directoryName); strcat(fileName, ENEMYEXPLOSIVEDROPSFILENAME); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); - if(!ReadInEnemyExplosiveDropsStats(gEnemyExplosiveDrops, fileName)) - return FALSE; + THROWIFFALSE(ReadInEnemyExplosiveDropsStats(gEnemyExplosiveDrops, fileName),ENEMYEXPLOSIVEDROPSFILENAME); strcpy(fileName, directoryName); strcat(fileName, ENEMYWEAPONDROPSFILENAME); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); - if(!ReadInEnemyWeaponDropsStats(gEnemyWeaponDrops, fileName)) - return FALSE; + THROWIFFALSE(ReadInEnemyWeaponDropsStats(gEnemyWeaponDrops, fileName),ENEMYWEAPONDROPSFILENAME); strcpy(fileName, directoryName); strcat(fileName, ENEMYAMMODROPSFILENAME); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); - if(!ReadInEnemyAmmoDropsStats(gEnemyAmmoDrops, fileName)) - return FALSE; + THROWIFFALSE(ReadInEnemyAmmoDropsStats(gEnemyAmmoDrops, fileName),ENEMYAMMODROPSFILENAME); strcpy(fileName, directoryName); strcat(fileName, ENEMYARMOURDROPSFILENAME); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); - if(!ReadInEnemyArmourDropsStats(gEnemyArmourDrops, fileName)) - return FALSE; + THROWIFFALSE(ReadInEnemyArmourDropsStats(gEnemyArmourDrops, fileName),ENEMYARMOURDROPSFILENAME); // WANNE: Enemy drops - end // WANNE: Sector Loadscreens [2007-05-18] @@ -163,8 +188,7 @@ BOOLEAN LoadExternalGameplayData(STR directoryName) if ( FileExists(fileName) ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); - if(!ReadInSectorLoadscreensStats(gSectorLoadscreens, fileName)) - return FALSE; + THROWIFFALSE(ReadInSectorLoadscreensStats(gSectorLoadscreens, fileName),SECTORLOADSCREENSFILENAME); } else { @@ -176,8 +200,7 @@ BOOLEAN LoadExternalGameplayData(STR directoryName) strcpy(fileName, directoryName); strcat(fileName, AMMOTYPESFILENAME); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); - if(!ReadInAmmoTypeStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInAmmoTypeStats(fileName),AMMOTYPESFILENAME); //Madd: Simple Localization //Read in the correct ammostring file for the given language @@ -213,8 +236,7 @@ BOOLEAN LoadExternalGameplayData(STR directoryName) //CHRISL: If we fail to load, try loading just the default english strcpy(fileName, directoryName); strcat(fileName, AMMOFILENAME); - if(!ReadInAmmoStats(fileName)) - return FALSE; + THROWIFFALSE(!ReadInAmmoStats(fileName),AMMOFILENAME); } @@ -222,15 +244,13 @@ BOOLEAN LoadExternalGameplayData(STR directoryName) strcpy(fileName, directoryName); strcat(fileName, BURSTSOUNDSFILENAME); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); - if(!ReadInBurstSoundArray(fileName)) - return FALSE; + THROWIFFALSE(ReadInBurstSoundArray(fileName),BURSTSOUNDSFILENAME); // Lesh: end strcpy(fileName, directoryName); strcat(fileName, ITEMSFILENAME); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); - if(!ReadInItemStats(fileName,FALSE)) - return FALSE; + THROWIFFALSE(ReadInItemStats(fileName,FALSE),ITEMSFILENAME); //Madd: Simple localization // The idea here is that we can have a separate xml file that's named differently @@ -241,31 +261,9 @@ BOOLEAN LoadExternalGameplayData(STR directoryName) // So for instance, the german file would be called German.Items.xml and would only contain // the uiIndex (for reference), szItemName, szLongItemName, szItemDesc, szBRName, and szBRDesc tags -#ifdef GERMAN - AddLanguagePrefix( fileName, GERMAN_PREFIX); -#endif -#ifdef RUSSIAN - AddLanguagePrefix( fileName, RUSSIAN_PREFIX); -#endif -#ifdef DUTCH - AddLanguagePrefix( fileName, DUTCH_PREFIX); -#endif -#ifdef POLISH - AddLanguagePrefix( fileName, POLISH_PREFIX); -#endif -#ifdef FRENCH - AddLanguagePrefix( fileName, FRENCH_PREFIX); -#endif -#ifdef ITALIAN - AddLanguagePrefix( fileName, ITALIAN_PREFIX); -#endif -#ifdef TAIWANESE - AddLanguagePrefix( fileName, TAIWANESE_PREFIX); -#endif -#ifdef CHINESE - AddLanguagePrefix( fileName, CHINESE_PREFIX); -#endif + #ifndef ENGLISH + AddLanguagePrefix(fileName); if ( FileExists(fileName) ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); @@ -279,97 +277,63 @@ BOOLEAN LoadExternalGameplayData(STR directoryName) strcpy(fileName, directoryName); strcat(fileName, SOUNDSFILENAME); - if(!ReadInSoundArray(fileName)) - return FALSE; + THROWIFFALSE(ReadInSoundArray(fileName),SOUNDSFILENAME); strcpy(fileName, directoryName); strcat(fileName, MAGAZINESFILENAME); - if(!ReadInMagazineStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInMagazineStats(fileName),MAGAZINESFILENAME); strcpy(fileName, directoryName); strcat(fileName, ATTACHMENTSFILENAME); - if(!ReadInAttachmentStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInAttachmentStats(fileName),ATTACHMENTSFILENAME); strcpy(fileName, directoryName); strcat(fileName, ATTACHMENTINFOFILENAME); - if(!ReadInAttachmentInfoStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInAttachmentInfoStats(fileName),ATTACHMENTINFOFILENAME); strcpy(fileName, directoryName); strcat(fileName, LAUNCHABLESFILENAME); - if(!ReadInLaunchableStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInLaunchableStats(fileName),LAUNCHABLESFILENAME); strcpy(fileName, directoryName); strcat(fileName, COMPATIBLEFACEITEMSFILENAME); - if(!ReadInCompatibleFaceItemStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInCompatibleFaceItemStats(fileName),COMPATIBLEFACEITEMSFILENAME); strcpy(fileName, directoryName); strcat(fileName, MERGESFILENAME); - if(!ReadInMergeStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInMergeStats(fileName),MERGESFILENAME); //if(!WriteMergeStats()) // return FALSE; strcpy(fileName, directoryName); strcat(fileName, ATTACHMENTCOMBOMERGESFILENAME); - if(!ReadInAttachmentComboMergeStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInAttachmentComboMergeStats(fileName),ATTACHMENTCOMBOMERGESFILENAME); strcpy(fileName, directoryName); strcat(fileName, EXPLOSIVESFILENAME); - if(!ReadInExplosiveStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInExplosiveStats(fileName),EXPLOSIVESFILENAME); strcpy(fileName, directoryName); strcat(fileName, ARMOURSFILENAME); - if(!ReadInArmourStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInArmourStats(fileName),ARMOURSFILENAME); // CHRISL: strcpy(fileName, directoryName); strcat(fileName, LOADBEARINGEQUIPMENTFILENAME); - if(!ReadInlbeStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInlbeStats(fileName),LOADBEARINGEQUIPMENTFILENAME); // CHRISL: + LBEPocketType.clear(); strcpy(fileName, directoryName); strcat(fileName, LBEPOCKETFILENAME); - if(!ReadInLBEPocketStats(fileName,FALSE)) - return FALSE; + THROWIFFALSE(ReadInLBEPocketStats(fileName,FALSE),LBEPOCKETFILENAME); //CHRISL: Simple localization // Same setup as what Madd used for items.xml -#ifdef GERMAN - AddLanguagePrefix( fileName, GERMAN_PREFIX); -#endif -#ifdef RUSSIAN - AddLanguagePrefix( fileName, RUSSIAN_PREFIX); -#endif -#ifdef DUTCH - AddLanguagePrefix( fileName, DUTCH_PREFIX); -#endif -#ifdef POLISH - AddLanguagePrefix( fileName, POLISH_PREFIX); -#endif -#ifdef FRENCH - AddLanguagePrefix( fileName, FRENCH_PREFIX); -#endif -#ifdef ITALIAN - AddLanguagePrefix( fileName, ITALIAN_PREFIX); -#endif -#ifdef TAIWANESE - AddLanguagePrefix( fileName, TAIWANESE_PREFIX); -#endif -#ifdef CHINESE - AddLanguagePrefix( fileName, CHINESE_PREFIX); -#endif #ifndef ENGLISH + AddLanguagePrefix(fileName); if ( FileExists(fileName) ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); @@ -381,142 +345,111 @@ BOOLEAN LoadExternalGameplayData(STR directoryName) // CHRISL: strcpy(fileName, directoryName); strcat(fileName, MERCSTARTINGGEARFILENAME); - if(!ReadInMercStartingGearStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInMercStartingGearStats(fileName),MERCSTARTINGGEARFILENAME); strcpy(fileName, directoryName); strcat(fileName, WEAPONSFILENAME); - if(!ReadInWeaponStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInWeaponStats(fileName),WEAPONSFILENAME); strcpy(fileName, directoryName); strcat(fileName, INCOMPATIBLEATTACHMENTSFILENAME); - if(!ReadInIncompatibleAttachmentStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInIncompatibleAttachmentStats(fileName),INCOMPATIBLEATTACHMENTSFILENAME); strcpy(fileName, directoryName); strcat(fileName, ENEMYGUNCHOICESFILENAME); - if(!ReadInExtendedArmyGunChoicesStats (fileName)) - return FALSE; + THROWIFFALSE(ReadInExtendedArmyGunChoicesStats (fileName),ENEMYGUNCHOICESFILENAME); strcpy(fileName, directoryName); strcat(fileName, ENEMYITEMCHOICESFILENAME); - if(!ReadInArmyItemChoicesStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInArmyItemChoicesStats(fileName),ENEMYITEMCHOICESFILENAME); strcpy(fileName, directoryName); strcat(fileName, IMPITEMCHOICESFILENAME); - if(!ReadInIMPItemChoicesStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInIMPItemChoicesStats(fileName),IMPITEMCHOICESFILENAME); strcpy(fileName, directoryName); strcat(fileName, TONYINVENTORYFILENAME); - if(!ReadInInventoryStats(gTonyInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gTonyInventory,fileName),TONYINVENTORYFILENAME); strcpy(fileName, directoryName); strcat(fileName, DEVININVENTORYFILENAME); - if(!ReadInInventoryStats(gDevinInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gDevinInventory,fileName),DEVININVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, FRANZINVENTORYFILENAME); - if(!ReadInInventoryStats(gFranzInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gFranzInventory,fileName),FRANZINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, KEITHINVENTORYFILENAME); - if(!ReadInInventoryStats(gKeithInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gKeithInventory,fileName),KEITHINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, SAMINVENTORYFILENAME); - if(!ReadInInventoryStats(gSamInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gSamInventory,fileName),SAMINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, JAKEINVENTORYFILENAME); - if(!ReadInInventoryStats(gJakeInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gJakeInventory,fileName),JAKEINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, HOWARDINVENTORYFILENAME); - if(!ReadInInventoryStats(gHowardInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gHowardInventory,fileName),HOWARDINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, GABBYINVENTORYFILENAME); - if(!ReadInInventoryStats(gGabbyInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gGabbyInventory,fileName),GABBYINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, FRANKINVENTORYFILENAME); - if(!ReadInInventoryStats(gFrankInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gFrankInventory,fileName),FRANKINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, ELGININVENTORYFILENAME); - if(!ReadInInventoryStats(gElginInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gElginInventory,fileName),ELGININVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, MANNYINVENTORYFILENAME); - if(!ReadInInventoryStats(gMannyInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gMannyInventory,fileName),MANNYINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, HERVEINVENTORYFILENAME); - if(!ReadInInventoryStats(gHerveInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gHerveInventory,fileName),HERVEINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, PETERINVENTORYFILENAME); - if(!ReadInInventoryStats(gPeterInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gPeterInventory,fileName),PETERINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, ALBERTOINVENTORYFILENAME); - if(!ReadInInventoryStats(gAlbertoInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gAlbertoInventory,fileName),ALBERTOINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, CARLOINVENTORYFILENAME); - if(!ReadInInventoryStats(gCarloInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gCarloInventory,fileName),CARLOINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, MICKEYINVENTORYFILENAME); - if(!ReadInInventoryStats(gMickyInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gMickyInventory,fileName),MICKEYINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, ARNIEINVENTORYFILENAME); - if(!ReadInInventoryStats(gArnieInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gArnieInventory,fileName),ARNIEINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, PERKOINVENTORYFILENAME); - if(!ReadInInventoryStats(gPerkoInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gPerkoInventory,fileName),PERKOINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, FREDOINVENTORYFILENAME); - if(!ReadInInventoryStats(gFredoInventory,fileName)) - return FALSE; + THROWIFFALSE(ReadInInventoryStats(gFredoInventory,fileName),FREDOINVENTORYFILENAME); + strcpy(fileName, directoryName); strcat(fileName, CITYTABLEFILENAME); - if(!ReadInMapStructure(fileName, FALSE)) - return FALSE; + THROWIFFALSE(ReadInMapStructure(fileName, FALSE),CITYTABLEFILENAME); -#ifdef GERMAN - AddLanguagePrefix( fileName, GERMAN_PREFIX); -#endif -#ifdef RUSSIAN - AddLanguagePrefix( fileName, RUSSIAN_PREFIX); -#endif -#ifdef DUTCH - AddLanguagePrefix( fileName, DUTCH_PREFIX); -#endif -#ifdef POLISH - AddLanguagePrefix( fileName, POLISH_PREFIX); -#endif -#ifdef FRENCH - AddLanguagePrefix( fileName, FRENCH_PREFIX); -#endif -#ifdef ITALIAN - AddLanguagePrefix( fileName, ITALIAN_PREFIX); -#endif -#ifdef TAIWANESE - AddLanguagePrefix( fileName, TAIWANESE_PREFIX); -#endif -#ifdef CHINESE - AddLanguagePrefix( fileName, CHINESE_PREFIX); -#endif #ifndef ENGLISH + AddLanguagePrefix(fileName); if ( FileExists(fileName) ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); @@ -532,90 +465,57 @@ BOOLEAN LoadExternalGameplayData(STR directoryName) // Lesh: load altsectors list strcpy(fileName, directoryName); strcat(fileName, ALTSECTORSFILENAME); - if ( !ReadInAltSectors(fileName) ) - return FALSE; + THROWIFFALSE(ReadInAltSectors(fileName),ALTSECTORSFILENAME); // Lesh: load samsites - must be after cities.xml strcpy(fileName, directoryName); strcat(fileName, SAMSITESFILENAME); - if ( !ReadInSAMInfo(fileName) ) - return FALSE; + THROWIFFALSE(ReadInSAMInfo(fileName),SAMSITESFILENAME); // Lesh: army externalization strcpy(fileName, directoryName); strcat(fileName, GARRISONFILENAME); - if ( !ReadInGarrisonInfo(fileName) ) - return FALSE; + THROWIFFALSE(ReadInGarrisonInfo(fileName),GARRISONFILENAME); strcpy(fileName, directoryName); strcat(fileName, PATROLFILENAME); - if ( !ReadInPatrolInfo(fileName) ) - return FALSE; + THROWIFFALSE(ReadInPatrolInfo(fileName),PATROLFILENAME); strcpy(fileName, directoryName); strcat(fileName, COMPOSITIONFILENAME); - if ( !ReadInArmyCompositionInfo(fileName) ) - return FALSE; + THROWIFFALSE(ReadInArmyCompositionInfo(fileName),COMPOSITIONFILENAME); strcpy(fileName, directoryName); strcat(fileName, EXPLOSIONDATAFILENAME); - if(!ReadInExplosionDataStats(fileName)) - return FALSE; + THROWIFFALSE(ReadInExplosionDataStats(fileName),EXPLOSIONDATAFILENAME); // Kaiden: Read in Restricted Sectors for Mobile Militia strcpy(fileName, directoryName); strcat(fileName, ROAMINGMILITIAFILENAME); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); - if(!ReadInRoamingInfo(fileName)) - return FALSE; + THROWIFFALSE(ReadInRoamingInfo(fileName),ROAMINGMILITIAFILENAME); // Dealtar: Read in shipping destinations and delivery methods + gPostalService.Clear(); strcpy(fileName, directoryName); strcat(fileName, SHIPPINGDESTINATIONSFILENAME); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); - if(!ReadInShippingDestinations(fileName, FALSE)) - return FALSE; + THROWIFFALSE(ReadInShippingDestinations(fileName, FALSE),SHIPPINGDESTINATIONSFILENAME); -#ifdef GERMAN - AddLanguagePrefix( fileName, GERMAN_PREFIX); -#endif -#ifdef RUSSIAN - AddLanguagePrefix( fileName, RUSSIAN_PREFIX); -#endif -#ifdef DUTCH - AddLanguagePrefix( fileName, DUTCH_PREFIX); -#endif -#ifdef POLISH - AddLanguagePrefix( fileName, POLISH_PREFIX); -#endif -#ifdef FRENCH - AddLanguagePrefix( fileName, FRENCH_PREFIX); -#endif -#ifdef ITALIAN - AddLanguagePrefix( fileName, ITALIAN_PREFIX); -#endif -#ifdef TAIWANESE - AddLanguagePrefix( fileName, TAIWANESE_PREFIX); -#endif -#ifdef CHINESE - AddLanguagePrefix( fileName, CHINESE_PREFIX); -#endif #ifndef ENGLISH + AddLanguagePrefix(fileName); if ( FileExists(fileName) ) { DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); - if(!ReadInShippingDestinations(fileName, TRUE)) - return FALSE; + THROWIFFALSE(ReadInShippingDestinations(fileName, TRUE),SHIPPINGDESTINATIONSFILENAME); } #endif - + strcpy(fileName, directoryName); strcat(fileName, DELIVERYMETHODSFILENAME); DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("LoadExternalGameplayData, fileName = %s", fileName)); - if(!ReadInDeliveryMethods(fileName)) - return FALSE; + THROWIFFALSE(ReadInDeliveryMethods(fileName),DELIVERYMETHODSFILENAME); - return TRUE; } @@ -632,10 +532,11 @@ UINT32 InitializeJA2(void) gfWorldLoaded = FALSE; //Load external game mechanic data - if ( !LoadExternalGameplayData(TABLEDATA_DIRECTORY)) - { - return( ERROR_SCREEN ); - } + //if ( !LoadExternalGameplayData(TABLEDATA_DIRECTORY)) + //{ + // return( ERROR_SCREEN ); + //} + TRYCATCH_RETHROW(LoadExternalGameplayData(TABLEDATA_DIRECTORY),L"Loading external data failed"); // Load external text LoadAllExternalText(); diff --git a/Laptop/AimMembers.cpp b/Laptop/AimMembers.cpp index f2f54ff5..604c5145 100644 --- a/Laptop/AimMembers.cpp +++ b/Laptop/AimMembers.cpp @@ -51,6 +51,8 @@ #include "fresh_header.h" #include "Encrypted File.h" +#include + // //****** Defines ****** // @@ -1252,7 +1254,17 @@ BOOLEAN DisplayMercsInventory(UINT8 ubMercID) pItem = &Item[ usItem ]; GetVideoObject( &hVObject, GetInterfaceGraphicForItem( pItem ) ); - pTrav = &(hVObject->pETRLEObject[ pItem->ubGraphicNum ] ); + if(pItem->ubGraphicNum < hVObject->usNumberOfObjects) + { + pTrav = &(hVObject->pETRLEObject[ pItem->ubGraphicNum ] ); + } + else + { + std::wstringstream wss; + wss << L"Number of images in VObject [" << hVObject->usNumberOfObjects + << L"] is smaller than the requested index [" << (int)pItem->ubGraphicNum << L"]"; + THROWEXCEPTION(wss.str().c_str()); + } usHeight = (UINT32)pTrav->usHeight; usWidth = (UINT32)pTrav->usWidth; diff --git a/Laptop/BobbyR.cpp b/Laptop/BobbyR.cpp index 66ca4aea..d114da60 100644 --- a/Laptop/BobbyR.cpp +++ b/Laptop/BobbyR.cpp @@ -186,7 +186,6 @@ extern MOUSE_REGION *gSelectedScrollAreaDropDownRegion; extern void BobbyRDeliveryCallback(RefToCShipmentManipulator ShipmentManipulator); //End Dealtar's Airport Externalization. - //Bobby's Sign menu mouse regions MOUSE_REGION gSelectedBobbiesSignMenuRegion[ BOBBIES_NUMBER_SIGNS ]; void SelectBobbiesSignMenuRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason ); @@ -254,8 +253,6 @@ BOOLEAN EnterBobbyR() //gDestinationTable.resize(gPostalService.LookupDestinationList().size()); } //End Dealtar's Airport Externalization. - - // an array of mouse regions for the bobbies signs. Top Left corner, bottom right corner UINT16 usMouseRegionPosArray[] = {BOBBIES_USED_SIGN_X, BOBBIES_USED_SIGN_Y, BOBBIES_USED_SIGN_X+BOBBIES_USED_SIGN_WIDTH, BOBBIES_USED_SIGN_Y+BOBBIES_USED_SIGN_HEIGHT, BOBBIES_MISC_SIGN_X, BOBBIES_MISC_SIGN_Y, BOBBIES_MISC_SIGN_X+BOBBIES_MISC_SIGN_WIDTH, BOBBIES_MISC_SIGN_Y+BOBBIES_MISC_SIGN_HEIGHT, diff --git a/Laptop/BobbyRMailOrder.cpp b/Laptop/BobbyRMailOrder.cpp index 11660424..5c6db3a7 100644 --- a/Laptop/BobbyRMailOrder.cpp +++ b/Laptop/BobbyRMailOrder.cpp @@ -342,6 +342,7 @@ void BobbyRDeliveryCallback(RefToCShipmentManipulator ShipmentManipulator) { // ScreenMsg(FONT_MCOLOR_RED, MSG_DEBUG, L"Shipment from Bobby Ray has arrived at %s!", ShipmentManipulator.GetDestination().wstrName.c_str()); gusCurShipmentDestinationID = ShipmentManipulator.GetDestination().usID; + // WANNE - MP: Do not send email notification from Bobby Ray in a multiplayer game if (!is_networked) AddEmail( BOBBYR_SHIPMENT_ARRIVED, BOBBYR_SHIPMENT_ARRIVED_LENGTH, BOBBY_R, GetWorldTotalMin(), -1 ); @@ -1143,7 +1144,6 @@ void DisplayShippingCosts( BOOLEAN fCalledFromOrderPage, INT32 iSubTotal, UINT16 default: usStandardCost = 0; } - iShippingCost = (INT32)( ( gpNewBobbyrShipments[ iOrderNum ].uiPackageWeight / (FLOAT)10 ) * usStandardCost + .5 ); * Dealtar's Code: */ UINT32 uiPackageWeight=0; @@ -1621,7 +1621,6 @@ void DrawSelectedCity( UINT8 ubCityNumber ) */ if( gubCityAtTopOfList+guiNumOfDisplayedCities > (UINT8)gPostalService.LookupDestinationList().size() ) gubCityAtTopOfList = gPostalService.LookupDestinationList().size() - guiNumOfDisplayedCities - 1; - //Display the list of cities @@ -1659,7 +1658,6 @@ void DrawSelectedCity( UINT8 ubCityNumber ) DrawTextToScreen( (STR16)gDestinationTable[0]->wstrName.c_str(), BOBBYR_CITY_START_LOCATION_X+BOBBYR_CITY_NAME_OFFSET, (UINT16)(usPosY+5), 0, BOBBYR_DROPDOWN_FONT, BOBBYR_FONT_BLACK, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); else DrawTextToScreen( (STR16)gDestinationTable[ubCityNumber]->wstrName.c_str(), BOBBYR_CITY_START_LOCATION_X+BOBBYR_CITY_NAME_OFFSET, (UINT16)(usPosY+5), 0, BOBBYR_DROPDOWN_FONT, BOBBYR_FONT_BLACK, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); - SetFontShadow(DEFAULT_SHADOW); @@ -2028,7 +2026,6 @@ UINT32 CalcCostFromWeightOfPackage( UINT8 ubTypeOfService ) } */ usStandardCost = gPostalService.GetDestinationFee(ubTypeOfService, gDestinationTable[gbSelectedCity]->usID); - //Get the actual weight ( either in lbs or metric ) /// usStandardCost = (UINT16) GetWeightBasedOnMetricOption( usStandardCost ); diff --git a/Laptop/IMP Attribute Selection.cpp b/Laptop/IMP Attribute Selection.cpp index 5bd89c11..638b19ff 100644 --- a/Laptop/IMP Attribute Selection.cpp +++ b/Laptop/IMP Attribute Selection.cpp @@ -24,7 +24,6 @@ #include "text.h" #include "GameSettings.h" #include "IMP Disability Trait.h" // Added this one - SANDRO - #endif #define STARTING_LEVEL_BOX_POS_X ( 51 ) diff --git a/Laptop/Laptop.vcproj b/Laptop/Laptop.vcproj index c2bc23ab..fae00d20 100644 --- a/Laptop/Laptop.vcproj +++ b/Laptop/Laptop.vcproj @@ -4491,9 +4491,6 @@ BrowseInformation="1"/> - - - - - - - - diff --git a/Laptop/PostalService.cpp b/Laptop/PostalService.cpp index fae8bf50..4411124d 100644 --- a/Laptop/PostalService.cpp +++ b/Laptop/PostalService.cpp @@ -14,6 +14,7 @@ // WANNE - MP: Used for multiplayer #include "connect.h" #include "Strategic Event Handler.h" + using namespace std; /************************************************************************************/ @@ -85,6 +86,7 @@ OBJECTTYPE CPostalService::tempObject; CPostalService::CPostalService() { } + void CPostalService::Clear() { _Shipments.clear(); @@ -95,6 +97,7 @@ void CPostalService::Clear() _DeliveryMethods.clear(); } + UINT16 CPostalService::CreateNewShipment(UINT16 usDestinationID, UINT8 ubDeliveryMethodIndex, INT16 sSenderID) { if( usDestinationID > _UsedDestinationIDList.size() || @@ -212,7 +215,7 @@ BOOLEAN CPostalService::SendShipment(UINT16 usShipmentID) } SHIPMENT(sli).ShipmentStatus = SHIPMENT_INTRANSIT; - + if (is_networked) { if (is_client) @@ -548,6 +551,7 @@ BOOLEAN CPostalService::DeliverShipmentForMultiplayer(UINT16 usShipmentID) return FALSE; } + BOOLEAN CPostalService::RegisterDeliveryCallback(INT16 sSenderID, PtrToDeliveryCallbackFunc DeliveryCallbackFunc) { if(sSenderID < - 1 || !DeliveryCallbackFunc) diff --git a/Laptop/email.cpp b/Laptop/email.cpp index 9c0f65ac..392c0273 100644 --- a/Laptop/email.cpp +++ b/Laptop/email.cpp @@ -24,7 +24,6 @@ #include "PostalService.h" #include #endif - using namespace std; //static EmailPtr pEmailList; diff --git a/Laptop/finances.cpp b/Laptop/finances.cpp index c026c86e..32f2b823 100644 --- a/Laptop/finances.cpp +++ b/Laptop/finances.cpp @@ -424,7 +424,7 @@ void GameInitFinances() if( (FileExists( FINANCES_DATA_FILE ) ) ) { FileClearAttributes( FINANCES_DATA_FILE ); - FileDelete( FINANCES_DATA_FILE ); + FileDelete( FINANCES_DATA_FILE ); } GetBalanceFromDisk( ); } @@ -1612,6 +1612,9 @@ void GetBalanceFromDisk( void ) HWFILE hFileHandle; INT32 iBytesRead=0; + if(!FileExists(FINANCES_DATA_FILE)) + return; + // open file hFileHandle=FileOpen( FINANCES_DATA_FILE,( FILE_OPEN_EXISTING | FILE_ACCESS_READ ), FALSE ); @@ -1620,7 +1623,7 @@ void GetBalanceFromDisk( void ) { LaptopSaveInfo.iCurrentBalance = 0; // close file - FileClose( hFileHandle ); + FileClose( hFileHandle ); return; } @@ -1708,8 +1711,8 @@ UINT32 ReadInLastElementOfFinanceListAndReturnIdNumber( void ) // make sure file is more than balance size + length of 1 record - 1 byte if ( FileGetSize( hFileHandle ) < sizeof( INT32 ) + sizeof( UINT32 ) + sizeof( UINT8 )+ sizeof(UINT8) + sizeof( INT32 ) ) { - FileClose( hFileHandle ); - return 0; + FileClose( hFileHandle ); + return 0; } // size is? diff --git a/Laptop/florist Order Form.cpp b/Laptop/florist Order Form.cpp index dcc3172b..79fd08f7 100644 --- a/Laptop/florist Order Form.cpp +++ b/Laptop/florist Order Form.cpp @@ -487,8 +487,8 @@ void ExitFloristOrderForm() RemoveButton( guiFlowerOrderGalleryButton ); //Store the text fields - Get16BitStringFromField( 1, gsSentimentTextField ); - Get16BitStringFromField( 2, gsNameTextField ); + Get16BitStringFromField( 1, gsSentimentTextField, 75 ); + Get16BitStringFromField( 2, gsNameTextField, 35 ); gbCurrentlySelectedCard = -1; @@ -1039,12 +1039,12 @@ BOOLEAN CreateDestroyFlowerOrderDestDropDown( UINT8 ubDropDownMode ) //if its the personel sentiment field if( ubTextFieldID == 1 ) { - Get16BitStringFromField( ubTextFieldID, gsSentimentTextField ); + Get16BitStringFromField( ubTextFieldID, gsSentimentTextField, 75 ); } else if( ubTextFieldID == 2 ) { //else its the name field - Get16BitStringFromField( ubTextFieldID, gsNameTextField ); + Get16BitStringFromField( ubTextFieldID, gsNameTextField, 35 ); } SetActiveField(0); @@ -1074,7 +1074,7 @@ BOOLEAN CreateDestroyFlowerOrderDestDropDown( UINT8 ubDropDownMode ) //disable the text entry fields // DisableAllTextFields(); - Get16BitStringFromField( 1, gsSentimentTextField ); + Get16BitStringFromField( 1, gsSentimentTextField, 75 ); KillTextInputMode(); //disable the clear order and accept order buttons, (their rendering interferes with the drop down graphics) @@ -1267,12 +1267,12 @@ void HandleFloristOrderKeyBoardInput() //if its the personel sentiment field if( ubTextFieldID == 1 ) { - Get16BitStringFromField( ubTextFieldID, gsSentimentTextField ); + Get16BitStringFromField( ubTextFieldID, gsSentimentTextField, 75 ); } else if( ubTextFieldID == 2 ) { //else its the name field - Get16BitStringFromField( ubTextFieldID, gsNameTextField ); + Get16BitStringFromField( ubTextFieldID, gsNameTextField, 35 ); } SetActiveField(0); diff --git a/Laptop/history.cpp b/Laptop/history.cpp index e09ce3ea..adddd507 100644 --- a/Laptop/history.cpp +++ b/Laptop/history.cpp @@ -19,7 +19,7 @@ #include "LaptopSave.h" #endif - +#include "connect.h" #define TOP_X LAPTOP_SCREEN_UL_X #define TOP_Y LAPTOP_SCREEN_UL_Y @@ -226,8 +226,8 @@ void GameInitHistory() FileDelete( HISTORY_DATA_FILE ); } - AddHistoryToPlayersLog(HISTORY_ACCEPTED_ASSIGNMENT_FROM_ENRICO, 0, GetWorldTotalMin( ), -1, -1); - + if (!is_networked) + AddHistoryToPlayersLog(HISTORY_ACCEPTED_ASSIGNMENT_FROM_ENRICO, 0, GetWorldTotalMin( ), -1, -1); } void EnterHistory() diff --git a/Laptop/personnel.cpp b/Laptop/personnel.cpp index ae566f85..5b44ae1e 100644 --- a/Laptop/personnel.cpp +++ b/Laptop/personnel.cpp @@ -578,7 +578,7 @@ void EnterPersonnel( void ) InitVariables(); // Clear out the current team list - for (unsigned idx=0; idx < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; idx++) { + for (unsigned idx=0; idx < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; idx++) { currentTeamList[idx] = -1; } diff --git a/MPChatScreen.cpp b/MPChatScreen.cpp index 655b050f..105cdf90 100644 --- a/MPChatScreen.cpp +++ b/MPChatScreen.cpp @@ -756,27 +756,71 @@ UINT32 MPChatScreenHandle( ) // carter, need key shortcuts for clearing up message boxes // Check for esc + bool bHandled; while (DequeueEvent(&InputEvent) == TRUE) { - if( !HandleTextInput( &InputEvent ) && InputEvent.usEvent == KEY_DOWN ) + bHandled = false; + if(InputEvent.usEvent == KEY_DOWN ) + { + if( ( InputEvent.usParam == ESC ) ) { - if( ( InputEvent.usParam == ESC ) || ( InputEvent.usParam == 'n') ) - { - // Exit messagebox - gChatBox.bHandled = MSG_BOX_RETURN_NO; - memset(gszChatBoxInputString,0,sizeof(CHAR16)*255); - } - - if( InputEvent.usParam == ENTER ) - { - // retrieve the string from the text box - Get16BitStringFromField( 0, gszChatBoxInputString ); // these indexes are based on the order created - // Exit messagebox - gChatBox.bHandled = MSG_BOX_RETURN_OK; - } - - + // Exit messagebox + gChatBox.bHandled = MSG_BOX_RETURN_NO; + memset(gszChatBoxInputString,0,sizeof(CHAR16)*255); + bHandled = true; } + + if( InputEvent.usParam == ENTER ) + { + // retrieve the string from the text box + Get16BitStringFromField( 0, gszChatBoxInputString, 255 ); // these indexes are based on the order created + // Exit messagebox + gChatBox.bHandled = MSG_BOX_RETURN_OK; + bHandled = true; + } + + // OJW - 20090403 - add better key control + UINT8 ubDesiredMessageIndex; + UINT8 ubNumMessages; + + ubNumMessages = GetRangeOfChatLogMessages(); + + if ( ubNumMessages > MAX_CHATLOG_MESSAGES ) + { + if (InputEvent.usParam == PGUP) + { + //move up a page + ChatScreenMsgScrollUp( MAX_CHATLOG_MESSAGES ); + bHandled = true; + } + + if (InputEvent.usParam == PGDN) + { + // move down a page + ChatScreenMsgScrollDown( MAX_CHATLOG_MESSAGES ); + bHandled = true; + } + + if (InputEvent.usParam == HOME) + { + // move to the beginning + ChangeCurrentChatScreenMessageIndex( 0 ); + bHandled = true; + } + + if (InputEvent.usParam == END) + { + // move to end + ubDesiredMessageIndex = ubNumMessages - MAX_CHATLOG_MESSAGES; + ChangeCurrentChatScreenMessageIndex( ubDesiredMessageIndex ); + bHandled = true; + } + } + } + + // send to text box + if (!bHandled) + HandleTextInput( &InputEvent ); } if ( gChatBox.bHandled ) @@ -839,7 +883,7 @@ void OKChatBoxCallback(GUI_BUTTON *btn, INT32 reason ) // OK, exit gChatBox.bHandled = MSG_BOX_RETURN_OK; // retrieve the string from the text box - Get16BitStringFromField( 0, gszChatBoxInputString ); // these indexes are based on the order created + Get16BitStringFromField( 0, gszChatBoxInputString, 255 ); // these indexes are based on the order created } else if ( reason & MSYS_CALLBACK_REASON_LOST_MOUSE ) { diff --git a/MPChatScreen.h b/MPChatScreen.h index 6fee1aee..8921fa99 100644 --- a/MPChatScreen.h +++ b/MPChatScreen.h @@ -12,6 +12,8 @@ extern BOOLEAN gbChatSendToAll; extern MESSAGE_BOX_STRUCT gChatBox; extern BOOLEAN gfInChatBox; +#define CHAT_FONT_COLOR 208 // WHITE + //////////////////////////////// // ubStyle: Determines the look of graphics including buttons // zString: 16-bit string diff --git a/MPConnectScreen.cpp b/MPConnectScreen.cpp new file mode 100644 index 00000000..8069233e --- /dev/null +++ b/MPConnectScreen.cpp @@ -0,0 +1,526 @@ +#ifdef PRECOMPILEDHEADERS + #include "JA2 All.h" + #include "Intro.h" +#else + #include "Types.h" + #include "MPConnectScreen.h" + #include "GameSettings.h" + #include "Utilities.h" + #include "wCheck.h" + #include "Font Control.h" + #include "WordWrap.h" + #include "Render Dirty.h" + #include "Input.h" + #include "Options Screen.h" + #include "English.h" + #include "Sysutil.h" + #include "Fade Screen.h" + #include "Cursor Control.h" + #include "Music Control.h" + #include "cursors.h" + #include "Intro.h" + #include "Text.h" + #include "Text Input.h" + #include "_Ja25EnglishText.h" + #include "Soldier Profile.h" + #include "Loading Screen.h" + #include "Animated ProgressBar.h" + #include "mainmenuscreen.h" +#endif + +#include "gameloop.h" +#include "Game Init.h" +#include "connect.h" +#include "network.h" // for client name +#include "saveloadscreen.h" +#include "message.h" +#include "init.h" +#include "xml.h" + + +//////////////////////////////////////////// +// +// Global Defines +// +/////////////////////////////////////////// + +#define MPC_TITLE_FONT FONT14ARIAL//FONT16ARIAL +#define MPC_TITLE_COLOR FONT_MCOLOR_WHITE + +#define MPC_SUBHEADING_FONT FONT12ARIAL//FONT16ARIAL +#define MPC_SUBHEADING_COLOR FONT_MCOLOR_WHITE + +//buttons +#define MPC_BTN_CANCEL_X iScreenWidthOffset + ((320 - 115) / 2) +#define MPC_BTN_CANCEL_Y iScreenHeightOffset + SCREEN_HEIGHT - 50 + +//main title +#define MPC_MAIN_TITLE_X 0 +#define MPC_MAIN_TITLE_Y iScreenHeightOffset + 10 +#define MPC_MAIN_TITLE_WIDTH SCREEN_WIDTH + +//subtitle +#define MPC_SUBHEADING_X 0 +#define MPC_SUBHEADING_Y MPC_MAIN_TITLE_Y + 30 +#define MPC_SUBHEADING_WIDTH SCREEN_WIDTH + +//help +#define MPC_HELP_X 0 +#define MPC_HELP_Y MPC_SUBHEADING_Y + 30 +#define MPC_HELP_WIDTH SCREEN_WIDTH + + +//////////////////////////////////////////// +// +// Global Variables +// +/////////////////////////////////////////// + +BOOLEAN gfMPCScreenEntry = TRUE; +BOOLEAN gfMPCScreenExit = FALSE; +BOOLEAN gfReRenderMPCScreen=TRUE; +BOOLEAN gfMPCButtonsAllocated = FALSE; +BOOLEAN gfMPConnectScreenCanContinue = FALSE; + +//enum for different states of screen +enum +{ + MPC_NOTHING, + MPC_CANCEL, + MPC_CONTINUE, +}; + +UINT8 gubMPCScreenHandler=MPC_NOTHING; // State changer for HandleMPCNScreen() + +UINT32 gubMPCExitScreen = MP_CONNECT_SCREEN; // The screen that is in control next iteration of the game_loop + +UINT8 loadscreenID = 0; + +//////////////////////////////////////////// +// +// Screen Controls +// +/////////////////////////////////////////// + +// Cancel Button +void BtnMPCCancelCallback(GUI_BUTTON *btn,INT32 reason); +UINT32 guiMPCCancelButton; +INT32 giMPCCancelBtnImage; + +UINT32 guiMPCMainBackGroundImage; + +// Message Box handle +INT8 giMPCMessageBox = -1; + +// connect screen message +CHAR16 gszConnectHeadingMsg[512]; +CHAR16 gszConnectSubMsg[512]; + +//////////////////////////////////////////// +// +// Local Function Prototypes +// +/////////////////////////////////////////// + +extern void ClearMainMenu(); + +BOOLEAN EnterMPCScreen(); +BOOLEAN ExitMPCScreen(); +void HandleMPCScreen(); +BOOLEAN RenderMPCScreen(); + +void GetMPCScreenUserInput(); +void DoneFadeOutForExitMPCScreen( void ); +void DoneFadeInForExitMPCScreen( void ); +void CheckForContinue(); + + +UINT32 MPConnectScreenInit( void ) +{ + return (1); +} + +UINT32 MPConnectScreenHandle( void ) +{ + StartFrameBufferRender(); + + RestoreBackgroundRects( ); + + if( gfMPCScreenEntry ) + { +// PauseGame(); + + EnterMPCScreen(); + gfMPCScreenEntry = FALSE; + gfMPCScreenExit = FALSE; + InvalidateRegion( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ); + } + + // add scrolling string log + ScrollString( ); + + + GetMPCScreenUserInput(); + + // connect to the server + NetworkAutoStart(); + + // Check if we need to leave this screen + CheckForContinue(); + + HandleMPCScreen(); + + // draw chat texts + ExecuteVideoOverlays( ); + + SaveBackgroundRects(); + //InvalidateScreen(); + + ExecuteBaseDirtyRectQueue(); + EndFrameBufferRender(); + + // handle fades in and out + if ( HandleFadeOutCallback( ) ) + { + ClearMainMenu(); + return( gubMPCExitScreen ); + } + + if ( HandleBeginFadeOut( gubMPCExitScreen ) ) + { + return( gubMPCExitScreen ); + } + + if ( HandleFadeInCallback( ) ) + { + // Re-render the scene! + RenderMPCScreen(); + } + + if ( HandleBeginFadeIn( gubMPCExitScreen ) ) + { + + } + + if( gfMPCScreenExit ) // we are exiting this screen + { + ExitMPCScreen(); // perform destruction + } + + return( gubMPCExitScreen ); +} // end MPScoreScreenHandle() + +UINT32 MPConnectScreenShutdown( void ) +{ + return( 1 ); +} + +BOOLEAN EnterMPCScreen() +{ + // make sure only chat messages show up on connect screen + ClearTacticalMessageQueue( ); + SetTacticalMessageFilter( MSG_CHAT ); + + gfMPConnectScreenCanContinue = FALSE; + + VOBJECT_DESC VObjectDesc; + + if( gfMPCButtonsAllocated ) + return( TRUE ); + + SetCurrentCursorFromDatabase( CURSOR_NORMAL ); + + // load the Main trade screen backgroiund image + VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; + if (iResolution == 0) + { + FilenameForBPP("LOADSCREENS\\LS_DayOmerta.sti",VObjectDesc.ImageFile); + } + else if (iResolution == 1) + { + FilenameForBPP("LOADSCREENS\\LS_DayOmerta_800x600.sti",VObjectDesc.ImageFile); + } + else if (iResolution == 2) + { + FilenameForBPP("LOADSCREENS\\LS_DayOmerta_1024x768.sti",VObjectDesc.ImageFile); + } + CHECKF(AddVideoObject(&VObjectDesc, &guiMPCMainBackGroundImage )); + + + CreateLoadingScreenProgressBar(); + // default to red connecting color + SetProgressBarColor(0,150,0,0); + SetRelativeStartAndEndPercentage(0,0,100,L""); + + //Cancel button + /*giMPCCancelBtnImage = LoadButtonImage("INTERFACE\\PreferencesButtons.sti", -1,0,-1,2,-1 ); + guiMPCCancelButton = CreateIconAndTextButton( giMPCCancelBtnImage, gzMPCScreenText[MPC_CANCEL_TEXT], OPT_BUTTON_FONT, + OPT_BUTTON_ON_COLOR, DEFAULT_SHADOW, + OPT_BUTTON_OFF_COLOR, DEFAULT_SHADOW, + TEXT_CJUSTIFIED, + MPC_BTN_CANCEL_X, MPC_BTN_CANCEL_Y, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, + DEFAULT_MOVE_CALLBACK, BtnMPCCancelCallback); + + SpecifyButtonSoundScheme( guiMPCCancelButton, BUTTON_SOUND_SCHEME_BIGSWITCH3 ); + SpecifyDisabledButtonStyle( guiMPCCancelButton, DISABLED_STYLE_NONE );*/ + + //Reset the exit screen - screen the main game loop will call next iteration + gubMPCExitScreen = MP_CONNECT_SCREEN; + + //REnder the screen once so we can blt ot to ths save buffer + RenderMPCScreen(); + + BlitBufferToBuffer(guiRENDERBUFFER, guiSAVEBUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ); + + gfMPCButtonsAllocated = TRUE; + + return( TRUE ); + +} // End of EnterMPSScreen() + + +BOOLEAN ExitMPCScreen() +{ + RemoveTacticalMessageFilter(); + + if( !gfMPCButtonsAllocated ) + return( TRUE ); + + //Delete the main options screen background + DeleteVideoObjectFromIndex( guiMPCMainBackGroundImage ); + RemoveProgressBar(0); + + //RemoveButton( guiMPCCancelButton ); + //UnloadButtonImage( giMPCCancelBtnImage ); + + // exit text input mode in this screen and clean up text boxes + //KillAllTextInputModes(); + SetTextInputCursor( CURSOR_IBEAM ); + + gfMPCButtonsAllocated = FALSE; + + //If we are starting the game stop playing the music + if( gubMPCScreenHandler == MPC_CONTINUE ) + SetMusicMode( MUSIC_NONE ); + + gfMPCScreenExit = FALSE; + gfMPCScreenEntry = TRUE; + + return( TRUE ); + +} // End of ExitMPJScreen() + + +void HandleMPCScreen() +{ + if( gubMPCScreenHandler != MPC_NOTHING ) + { + switch( gubMPCScreenHandler ) + { + case MPC_CANCEL: + gubMPCExitScreen = MP_JOIN_SCREEN; + gfMPCScreenExit = TRUE; + + // disconnect the client + client_disconnect(); + break; + + case MPC_CONTINUE: + { + //if we are already fading out, get out of here + if( gFadeOutDoneCallback != DoneFadeOutForExitMPCScreen ) + { + //Disable the ok button + DisableButton( guiMPCCancelButton ); + + gFadeOutDoneCallback = DoneFadeOutForExitMPCScreen; + + FadeOutNextFrame( ); + } + break; + } + + } + + gubMPCScreenHandler = MPC_NOTHING; + } + + if( gfReRenderMPCScreen ) + { + RenderMPCScreen(); + gfReRenderMPCScreen = FALSE; + } + +} // end of HandleMPCScreen + + +BOOLEAN RenderMPCScreen() +{ + HVOBJECT hPixHandle; + + //Get the main background screen graphic and blt it + GetVideoObject(&hPixHandle, guiMPCMainBackGroundImage ); + BltVideoObject(FRAME_BUFFER, hPixHandle, 0,0,0, VO_BLT_SRCTRANSPARENCY,NULL); + //DisplayLoadScreenWithID( loadscreenID ); + + //Shade the background + //ShadowVideoSurfaceRect( FRAME_BUFFER, iScreenWidthOffset, iScreenHeightOffset, iScreenWidthOffset + 640, iScreenHeightOffset + 480 ); + + UINT8 iProgress = 0; + + if (is_connecting) + { + iProgress = (UINT8)(100 * (float)((MAX_CONNECT_RETRIES-(float)giNumTries)/MAX_CONNECT_RETRIES)); + SetProgressBarColor(0,150,0,0); + + SetConnectScreenHeadingW(gzMPCScreenText[ MPC_CONNECTING_TEXT ]); + } + else if (is_connected) + { + if (!recieved_settings && !recieved_transfer_settings) + { + //Display the title + SetConnectScreenHeadingW( gzMPCScreenText[ MPC_GETSETTINGS_TEXT ]); + } + else + { + if (serverSyncClientsDirectory==1) + { + SetConnectScreenHeadingW( gzMPCScreenText[ MPC_DOWNLOADING_TEXT ]); + SetProgressBarColor(0,0,0,150); + iProgress = client_progress[CLIENT_NUM-1]; + } + else + { + SetConnectScreenHeadingW( gzMPCScreenText[ MPC_READY_TEXT ] ); + } + } + } + + //Display the title + DrawTextToScreen( gszConnectHeadingMsg, MPC_MAIN_TITLE_X, MPC_MAIN_TITLE_Y, MPC_MAIN_TITLE_WIDTH, MPC_TITLE_FONT, MPC_TITLE_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED ); + InvalidateRegion(MPC_MAIN_TITLE_X,MPC_MAIN_TITLE_Y,MPC_MAIN_TITLE_WIDTH,MPC_MAIN_TITLE_Y+20); + //Display the subheading + DrawTextToScreen( gszConnectSubMsg, MPC_SUBHEADING_X, MPC_SUBHEADING_Y, MPC_SUBHEADING_WIDTH, MPC_SUBHEADING_FONT, MPC_SUBHEADING_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED ); + InvalidateRegion(MPC_SUBHEADING_X,MPC_SUBHEADING_Y,MPC_MAIN_TITLE_WIDTH,MPC_SUBHEADING_Y+20); + //Display the help text + DrawTextToScreen( (is_connected && recieved_settings ? gzMPCScreenText[ MPC_HELP1_TEXT ] : gzMPCScreenText[MPC_HELP2_TEXT] ), MPC_HELP_X, MPC_HELP_Y, MPC_HELP_WIDTH, MPC_SUBHEADING_FONT, MPC_SUBHEADING_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED ); + InvalidateRegion(MPC_HELP_X,MPC_HELP_Y,MPC_HELP_WIDTH,MPC_HELP_Y+20); + + SetProgressBarRenderBuffer(0,FRAME_BUFFER); + RenderProgressBar(0,iProgress); + + return( TRUE ); +} // end of RenderMPCScreen() + + + +void GetMPCScreenUserInput() +{ + InputAtom Event; +// POINT MousePos; + +// GetCursorPos(&MousePos); + + while( DequeueEvent( &Event ) ) + { + // check if this event is swallowed by text input, otherwise process key + if( Event.usEvent == KEY_DOWN ) + { + switch( Event.usParam ) + { + + case ESC: + //Exit out of the screen + gubMPCScreenHandler = MPC_CANCEL; + break; + case 'y': + if (is_connected && recieved_settings) + OpenChatMsgBox(); + break; + } + } + } +} // end of GetMPCScreenUserInput() + +void DoneFadeOutForExitMPCScreen( void ) +{ + // ------------------------------------------ + // Now Initialize the client again with all the files from the "_MULTIPLAYER" profile!!! + LoadExternalGameplayData(TABLEDATA_DIRECTORY); + InitDependingGameStyleOptions(); + InitNewGame(false); + + // Overwrite some client settings with values from the server (cash, ...) + reapplySETTINGS(); + // ------------------------------------------ + + gubMPCExitScreen = MAP_SCREEN; + ExitMPCScreen(); // cleanup please, if we called a fadeout then we didnt do it above + SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR ); +} + +void DoneFadeInForExitMPCScreen( void ) +{ + SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR ); +} + +void BtnMPCCancelCallback(GUI_BUTTON *btn,INT32 reason) +{ + if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) + { + btn->uiFlags |= BUTTON_CLICKED_ON; + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } + if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + btn->uiFlags &= (~BUTTON_CLICKED_ON ); + + gubMPCScreenHandler = MPC_CANCEL; + + InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); + } + +} + +void SetConnectScreenHeadingW( STR16 cmsg ) +{ + wcscpy(gszConnectHeadingMsg,cmsg); + gfReRenderMPCScreen=TRUE; +} + +void SetConnectScreenHeadingA( const char* cmsg ) +{ + CHAR16 converted[512]; + MultiByteToWideChar( CP_UTF8, 0, cmsg, -1, (LPWSTR)converted, 512); + SetConnectScreenHeadingW( converted ); +} + +void SetConnectScreenSubMessageW( STR16 cmsg ) +{ + wcscpy(gszConnectSubMsg,cmsg); + gfReRenderMPCScreen=TRUE; +} + +void SetConnectScreenSubMessageA( const char* cmsg ) +{ + CHAR16 converted[512]; + MultiByteToWideChar( CP_UTF8, 0, cmsg, -1, (LPWSTR)converted, 512); + SetConnectScreenSubMessageW( converted ); +} + +void CheckForContinue() +{ + if (is_connected && is_client && recieved_settings) + { + if (serverSyncClientsDirectory == 1) + { + if (fClientReceivedAllFiles) + gubMPCScreenHandler = MPC_CONTINUE; + } + else + { + gubMPCScreenHandler = MPC_CONTINUE; + } + } +} diff --git a/MPConnectScreen.h b/MPConnectScreen.h new file mode 100644 index 00000000..e0412279 --- /dev/null +++ b/MPConnectScreen.h @@ -0,0 +1,14 @@ +#ifndef _MP_CONNECT_SCREEN_H_ +#define _MP_CONNECT_SCREEN_H_ + + +UINT32 MPConnectScreenInit( void ); +UINT32 MPConnectScreenHandle( void ); +UINT32 MPConnectScreenShutdown( void ); + +void SetConnectScreenHeadingW( STR16 cmsg ); +void SetConnectScreenHeadingA( const char* cmsg ); +void SetConnectScreenSubMessageW( STR16 cmsg ); +void SetConnectScreenSubMessageA( const char* cmsg ); + +#endif \ No newline at end of file diff --git a/MPHostScreen.cpp b/MPHostScreen.cpp index 1f1a51be..077e4735 100644 --- a/MPHostScreen.cpp +++ b/MPHostScreen.cpp @@ -30,6 +30,14 @@ #include "network.h" // for client name #include "saveloadscreen.h" +#include "VFS/vfs.h" +#include "VFS/vfs_init.h" +#include "VFS/PropertyContainer.h" +#include "VFS/iteratedir.h" +#include "MPJoinScreen.h" +#include "MainMenuScreen.h" +#include "Init.h" +#include "xml.h" //////////////////////////////////////////// // @@ -79,6 +87,10 @@ #define MPH_TXT_TIMER_Y MPH_TXT_DMG_Y + MPH_TXT_DMG_HEIGHT + 13 #define MPH_TXT_TIMER_WIDTH 60 #define MPH_TXT_TIMER_HEIGHT 17 +#define MPH_TXT_FILE_TRANSFER_DIR_X iScreenWidthOffset + 100 +#define MPH_TXT_FILE_TRANSFER_DIR_Y MPH_TXT_TIMER_Y + MPH_TXT_TIMER_HEIGHT + 13 +#define MPH_TXT_FILE_TRANSFER_DIR_WIDTH 200 +#define MPH_TXT_FILE_TRANSFER_DIR_HEIGHT 17 @@ -116,6 +128,10 @@ #define MPH_LABEL_TIMER_Y MPH_TXT_TIMER_Y + 3 #define MPH_LABEL_TIMER_WIDTH 80 #define MPH_LABEL_TIMER_HEIGHT 17 +#define MPH_LABEL_FILE_TRANSFER_DIR_X MPH_TXT_FILE_TRANSFER_DIR_X - 80 +#define MPH_LABEL_FILE_TRANSFER_DIR_Y MPH_TXT_FILE_TRANSFER_DIR_Y + 3 +#define MPH_LABEL_FILE_TRANSFER_DIR_WIDTH 80 +#define MPH_LABEL_FILE_TRANSFER_DIR_HEIGHT 17 //radio box locations #define MPH_GAP_BN_SETTINGS 35 @@ -131,8 +147,12 @@ #define MPH_GAMETYPE_SETTINGS_Y iScreenHeightOffset + 75 #define MPH_GAMETYPE_SETTINGS_WIDTH MPH_OFFSET_TO_TOGGLE_BOX - MPH_OFFSET_TO_TEXT +#define MPH_OVERRIDEMAXAI_X MPH_DIF_SETTINGS_X +#define MPH_OVERRIDEMAXAI_Y MPH_TXT_SVRNAME_Y +#define MPH_OVERRIDEMAXAI_WIDTH MPH_OFFSET_TO_TOGGLE_BOX + #define MPH_RNDMERC_X MPH_DIF_SETTINGS_X -#define MPH_RNDMERC_Y MPH_TXT_MAXPLAYERS_Y//iScreenHeightOffset+214 +#define MPH_RNDMERC_Y MPH_OVERRIDEMAXAI_Y + MPH_GAP_BN_SETTINGS - 5 #define MPH_RNDMERC_WIDTH MPH_OFFSET_TO_TOGGLE_BOX #define MPH_SAMEMERC_X MPH_DIF_SETTINGS_X @@ -159,14 +179,16 @@ #define MPH_USENIV_Y MPH_CIVS_Y + MPH_GAP_BN_SETTINGS - 5 #define MPH_USENIV_WIDTH MPH_OFFSET_TO_TOGGLE_BOX +#define MPH_SEND_FILES_X MPH_DIF_SETTINGS_X +#define MPH_SEND_FILES_Y MPH_USENIV_Y + MPH_GAP_BN_SETTINGS - 5 +#define MPH_SEND_FILES_WIDTH MPH_OFFSET_TO_TOGGLE_BOX + //Difficulty settings enum { - MPH_DIFF_EASY, - MPH_DIFF_MED, MPH_DIFF_HARD, MPH_DIFF_INSANE, - NUM_DIFF_SETTINGS, + MPH_NUM_DIFF_SETTINGS, }; @@ -200,17 +222,21 @@ UINT32 guiMPHMainBackGroundImage; CHAR16 gzServerNameField[ 30 ] = {0} ; CHAR16 gzMaxPlayersField[ 2 ] = {0} ; CHAR16 gzSquadSizeField[ 2 ] = {0} ; -CHAR16 gzTimeOfDayField[ 5 ] = {0}; +CHAR16 gzTimeOfDayField[ 6 ] = {0}; CHAR16 gzStartingBalanceField[ 10 ] = {0}; INT16 giMPHTimeHours = 0; INT16 giMPHTimeMins = 0; CHAR16 gzDmgMultiplierField[ 5 ] = {0}; -CHAR16 gzTimerField[ 5 ] = {0}; +CHAR16 gzTimerField[ 6 ] = {0}; +CHAR16 gzFileTransferDirectory[ 100 ] = {0} ; +CHAR16 gzKitBag[ 100 ] = {0}; UINT8 guiMPHGameType = MP_TYPE_DEATHMATCH; // default value +UINT8 guiMPHDifficultLevel = DIF_LEVEL_HARD; // Expert INT32 giMPHMessageBox = -1; // message box handle +INT giMPHOverrideMaxAI = 0; INT giMPHRandomMercs = 0; INT giMPHSameMercs = 0; INT giMPHReportMercs = 0; @@ -218,6 +244,7 @@ INT giMPHBobbyRays = 0; INT giMPHRandomSpawn = 0; INT giMPHEnableCivilians = 0; INT giMPHUseNIV = 0; +INT giMPHSendFiles = 0; //////////////////////////////////////////// @@ -237,13 +264,17 @@ UINT32 guiMPHCancelButton; INT32 giMPHCancelBtnImage; //checkbox to toggle the Diff level -UINT32 guiMPHDifficultySettingsToggles[ NUM_DIFF_SETTINGS ]; +UINT32 guiMPHDifficultySettingsToggles[ MPH_NUM_DIFF_SETTINGS ]; void BtnMPHDifficultyTogglesCallback(GUI_BUTTON *btn,INT32 reason); //checkbox to toggle Game Type UINT32 guiMPHGameTypeToggles[ NUM_MP_GAMETYPE ]; void BtnMPHGameTypeTogglesCallback(GUI_BUTTON *btn,INT32 reason); +//checkbox for override max ai +UINT32 guiMPHOverrideMaxAIToggle; +void BtnMPHOverrideMaxAICallback(GUI_BUTTON *btn,INT32 reason); + //checkbox for merc options UINT32 guiMPHRandomMercsToggle; void BtnMPHRandomMercCallback(GUI_BUTTON *btn,INT32 reason); @@ -272,6 +303,10 @@ void BtnMPHCivsCallback(GUI_BUTTON *btn,INT32 reason); UINT32 guiMPHUseNIVToggle; void BtnMPHUseNIVCallback(GUI_BUTTON *btn,INT32 reason); +//checkbox for send files +UINT32 guiMPHSendFiles; +void BtnMPHSendFilesCallback(GUI_BUTTON *btn,INT32 reason); + //////////////////////////////////////////// // @@ -303,10 +338,11 @@ void DoneFadeInForExitMPHScreen( void ); void SaveMPSettings() { - Get16BitStringFromField( 0, gzServerNameField ); // these indexes are based on the order created - Get16BitStringFromField( 1, gzMaxPlayersField ); - Get16BitStringFromField( 2, gzSquadSizeField ); - + Get16BitStringFromField( 0, gzServerNameField, 30 ); // these indexes are based on the order created + Get16BitStringFromField( 1, gzMaxPlayersField, 2 ); + Get16BitStringFromField( 2, gzSquadSizeField, 2 ); + Get16BitStringFromField( 7, gzFileTransferDirectory, 100 ); +#ifndef USE_VFS // save settings to JA2_mp.ini WritePrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_NAME", gzServerNameField, L"..\\Ja2_mp.ini" ); WritePrivateProfileStringW( L"Ja2_mp Settings",L"MAX_CLIENTS", gzMaxPlayersField, L"..\\Ja2_mp.ini" ); @@ -314,6 +350,9 @@ void SaveMPSettings() WritePrivateProfileStringW( L"Ja2_mp Settings",L"STARTING_BALANCE", gzStartingBalanceField, L"..\\Ja2_mp.ini" ); WritePrivateProfileStringW( L"Ja2_mp Settings",L"DAMAGE_MULTIPLIER", gzDmgMultiplierField, L"..\\Ja2_mp.ini" ); WritePrivateProfileStringW( L"Ja2_mp Settings",L"TIMED_TURN_SECS_PER_TICK", gzTimerField, L"..\\Ja2_mp.ini" ); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"FILE_TRANSFER_DIRECTORY", gzFileTransferDirectory , L"..\\Ja2_mp.ini" ); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"KIT_BAG", gzKitBag , L"..\\Ja2_mp.ini" ); + guiMPHGameType = GetMPHGameTypeButtonSetting(); @@ -327,6 +366,10 @@ void SaveMPSettings() CHAR16 tmpVal[2]; + giMPHOverrideMaxAI = ( ButtonList[ guiMPHOverrideMaxAIToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHOverrideMaxAI,tmpVal,10); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"OVERRIDE_MAX_AI", tmpVal, L"..\\Ja2_mp.ini" ); + giMPHRandomMercs = ( ButtonList[ guiMPHRandomMercsToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); _itow(giMPHRandomMercs,tmpVal,10); WritePrivateProfileStringW( L"Ja2_mp Settings",L"RANDOM_MERCS", tmpVal, L"..\\Ja2_mp.ini" ); @@ -355,22 +398,89 @@ void SaveMPSettings() _itow(giMPHUseNIV,tmpVal,10); WritePrivateProfileStringW( L"Ja2_mp Settings",L"ALLOW_CUSTOM_NIV", tmpVal, L"..\\Ja2_mp.ini" ); + giMPHSendFiles = ( ButtonList[ guiMPHSendFiles ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHSendFiles,tmpVal,10); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"SYNC_CLIENTS_MP_DIR", tmpVal, L"..\\Ja2_mp.ini" ); + guiMPHDifficultLevel = GetMPHCurrentDifficultyButtonSetting(); + CHAR16 tmpDiffStr[2]; + _itow(guiMPHDifficultLevel,tmpDiffStr,10); + WritePrivateProfileStringW( L"Ja2_mp Settings",L"DIFFICULT_LEVEL", tmpDiffStr, L"..\\Ja2_mp.ini" ); +#else + CPropertyContainer props; + props.InitFromIniFile("Ja2_mp.ini"); + + props.SetStringProperty(L"Ja2_mp Settings",L"SERVER_NAME", gzServerNameField); + props.SetStringProperty(L"Ja2_mp Settings",L"MAX_CLIENTS", gzMaxPlayersField); + props.SetStringProperty(L"Ja2_mp Settings",L"MAX_MERCS", gzSquadSizeField); + props.SetStringProperty(L"Ja2_mp Settings",L"STARTING_BALANCE", gzStartingBalanceField); + props.SetStringProperty(L"Ja2_mp Settings",L"DAMAGE_MULTIPLIER", gzDmgMultiplierField); + props.SetStringProperty(L"Ja2_mp Settings",L"TIMED_TURN_SECS_PER_TICK", gzTimerField); + props.SetStringProperty(L"Ja2_mp Settings",L"FILE_TRANSFER_DIRECTORY", gzFileTransferDirectory); + props.SetStringProperty(L"Ja2_mp Settings",L"KIT_BAG", gzKitBag); + + + guiMPHGameType = GetMPHGameTypeButtonSetting(); + CHAR16 tmpGTStr[2]; + _itow(guiMPHGameType,tmpGTStr,10); + props.SetStringProperty(L"Ja2_mp Settings",L"GAME_MODE", tmpGTStr); + + CHAR16 tmpTimeStr[6]; + swprintf(tmpTimeStr,L"%i.%i",giMPHTimeHours,giMPHTimeMins); + props.SetStringProperty(L"Ja2_mp Settings",L"TIME", tmpTimeStr); + + CHAR16 tmpVal[2]; + + giMPHOverrideMaxAI = ( ButtonList[ guiMPHOverrideMaxAIToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHOverrideMaxAI,tmpVal,10); + props.SetStringProperty(L"Ja2_mp Settings",L"OVERRIDE_MAX_AI", tmpVal); + + giMPHRandomMercs = ( ButtonList[ guiMPHRandomMercsToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHRandomMercs,tmpVal,10); + props.SetStringProperty(L"Ja2_mp Settings",L"RANDOM_MERCS", tmpVal); + + giMPHSameMercs = ( ButtonList[ guiMPHSameMercToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHSameMercs,tmpVal,10); + props.SetStringProperty(L"Ja2_mp Settings",L"SAME_MERC", tmpVal); + + giMPHBobbyRays = ( ButtonList[ guiMPHBobbyRayToggle ]->uiFlags & BUTTON_CLICKED_ON ? 0 : 1 ); // This setting is reversed + _itow(giMPHBobbyRays,tmpVal,10); + props.SetStringProperty(L"Ja2_mp Settings",L"DISABLE_BOBBY_RAYS", tmpVal); + + giMPHReportMercs = ( ButtonList[ guiMPHReportMercToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHReportMercs,tmpVal,10); + props.SetStringProperty(L"Ja2_mp Settings",L"REPORT_NAME", tmpVal); + + giMPHRandomSpawn = ( ButtonList[ guiMPHRandomSpawnToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHRandomSpawn,tmpVal,10); + props.SetStringProperty(L"Ja2_mp Settings",L"RANDOM_EDGES", tmpVal); + + giMPHEnableCivilians = ( ButtonList[ guiMPHCivsToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHEnableCivilians,tmpVal,10); + props.SetStringProperty(L"Ja2_mp Settings",L"CIV_ENABLED", tmpVal); + + giMPHUseNIV = ( ButtonList[ guiMPHUseNIVToggle ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHUseNIV,tmpVal,10); + props.SetStringProperty(L"Ja2_mp Settings",L"ALLOW_CUSTOM_NIV", tmpVal); + + giMPHSendFiles = ( ButtonList[ guiMPHSendFiles ]->uiFlags & BUTTON_CLICKED_ON ? 1 : 0 ); + _itow(giMPHSendFiles,tmpVal,10); + props.SetStringProperty(L"Ja2_mp Settings",L"SYNC_CLIENTS_MP_DIR", tmpVal); + + guiMPHDifficultLevel = GetMPHCurrentDifficultyButtonSetting(); + CHAR16 tmpDiffStr[2]; + _itow(guiMPHDifficultLevel,tmpDiffStr,10); + props.SetStringProperty(L"Ja2_mp Settings",L"DIFFICULT_LEVEL", tmpDiffStr); + props.WriteToIniFile(L"Ja2_mp.ini",true); // This writes to the Profiles/UserProfile/ja2_mp.ini +#endif - /*if ( ButtonList[ guiMPHRandomSpawnToggle ]->uiFlags & BUTTON_CLICKED_ON ) - WritePrivateProfileStringW( L"Ja2_mp Settings",L"RANDOM_EDGES", L"1", L"..\\Ja2_mp.ini" ); - else - WritePrivateProfileStringW( L"Ja2_mp Settings",L"RANDOM_EDGES", L"0", L"..\\Ja2_mp.ini" );*/ - - // save game difficulty setting - gGameOptions.ubDifficultyLevel = GetMPHCurrentDifficultyButtonSetting(); } bool ValidateMPSettings() { // Check a Server name is entered - Get16BitStringFromField( 0, gzServerNameField ); // these indexes are based on the order created + Get16BitStringFromField( 0, gzServerNameField, 30 ); // these indexes are based on the order created if (wcscmp(gzServerNameField,L"")<=0) { DoMPHMessageBox( MSG_BOX_BASIC_STYLE, gzMPHScreenText[MPH_SERVERNAME_INVALID], MP_HOST_SCREEN, MSG_BOX_FLAG_OK, NULL ); @@ -378,7 +488,7 @@ bool ValidateMPSettings() } // Verify the Max Players - Get16BitStringFromField( 1, gzMaxPlayersField ); + Get16BitStringFromField( 1, gzMaxPlayersField, 2 ); UINT8 numPlayers = _wtoi(gzMaxPlayersField); if (numPlayers < 2 || numPlayers > 4) { @@ -387,46 +497,61 @@ bool ValidateMPSettings() } // Verify the Squad Size - Get16BitStringFromField( 2, gzSquadSizeField ); + Get16BitStringFromField( 2, gzSquadSizeField, 2 ); UINT8 squadSize = _wtoi(gzSquadSizeField); - if (squadSize < 1 || squadSize > 6) + if (squadSize < 1 || squadSize > 5) { DoMPHMessageBox( MSG_BOX_BASIC_STYLE, gzMPHScreenText[MPH_SQUADSIZE_INVALID], MP_HOST_SCREEN, MSG_BOX_FLAG_OK, NULL ); return false; } // verify the Time of Day - Get16BitStringFromField( 3, gzTimeOfDayField ); + Get16BitStringFromField( 3, gzTimeOfDayField, 6 ); wchar_t* tok; bool bTimeOK = true; int hours = 0; int mins = 0; // strtok is destructive, make a copy to work on - CHAR16 tmpTODStr[5]; + CHAR16 tmpTODStr[6]; memcpy(tmpTODStr,gzTimeOfDayField,sizeof(CHAR16)*5); tok = wcstok(tmpTODStr,L":"); if (tok != NULL) { - hours = _wtoi(tok); - // check for invalid conversion, ie alpha chars - // wtoi returns 0 if it cant convert, but we need this value - // therefore if tok <> 0 then it was a bad convert. - if (hours == 0 && wcscmp(tok,L"0") != 0) + // Special case, because _wtoi() returns 0 if invalid number! + if (wcscmp(tok, L"00") == 0) { - // force error - bTimeOK = false; + hours = 0; + } + else + { + hours = _wtoi(tok); + // check for invalid conversion, ie alpha chars + // wtoi returns 0 if it cant convert, but we need this value + // therefore if tok <> 0 then it was a bad convert. + if (hours == 0 && wcscmp(tok,L"0") != 0) + { + // force error + bTimeOK = false; + } } tok = wcstok(NULL,L"."); if (tok != NULL) { - mins = _wtoi(tok); - if (mins == 0 && wcscmp(tok,L"0") != 0) + if (wcsnlen(tok, 50) >= 2 && tok[0] == '0' && tok[1] == '0') { - // force error - bTimeOK = false; + mins = 0; + } + else + { + mins = _wtoi(tok); + if (mins == 0 && wcscmp(tok,L"0") != 0) + { + // force error + bTimeOK = false; + } } // fix for single digits @@ -459,7 +584,7 @@ bool ValidateMPSettings() // verify the Starting Balance - Get16BitStringFromField( 4, gzStartingBalanceField ); + Get16BitStringFromField( 4, gzStartingBalanceField, 10 ); UINT32 sBalance = _wtoi(gzStartingBalanceField); if (sBalance < 1) { @@ -467,7 +592,7 @@ bool ValidateMPSettings() return false; } - Get16BitStringFromField( 5, gzDmgMultiplierField ); + Get16BitStringFromField( 5, gzDmgMultiplierField, 5 ); double fDmg = _wtof(gzDmgMultiplierField); if (fDmg <= 0.0f || fDmg >= 5.0f) { @@ -475,7 +600,7 @@ bool ValidateMPSettings() return false; } - Get16BitStringFromField( 6, gzTimerField ); + Get16BitStringFromField( 6, gzTimerField, 6 ); UINT32 iTimer = _wtoi(gzTimerField); if (iTimer < 1 || iTimer > 200) { @@ -483,39 +608,105 @@ bool ValidateMPSettings() return false; } + // Verify the File Transfer Directory + Get16BitStringFromField( 7, gzFileTransferDirectory, 100 ); + if (wcscmp(gzFileTransferDirectory,L"")<=0) + { + DoMPHMessageBox( MSG_BOX_BASIC_STYLE, gzMPHScreenText[MPH_FILE_TRANSFER_DIR_INVALID], MP_HOST_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } + +#ifdef USE_VFS + vfs::Path sUserDir(gzFileTransferDirectory); + if(!os::CreateRealDirecory(sUserDir,true)) + { + DoMPHMessageBox( MSG_BOX_BASIC_STYLE, gzMPHScreenText[MPH_FILE_TRANSFER_DIR_NOT_EXIST], MP_HOST_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } +#endif + + //if (!bSkipSyncDir) + //{ +#ifndef USE_VFS + // Check if sync folder exists + STRING512 syncDir; + STRING512 executableDir; + + GetExecutableDirectory(executableDir); + sprintf(syncDir, "%s\\%S", executableDir, gzFileTransferDirectory); + + if (!DirectoryExists(syncDir)) + { + DoMPHMessageBox( MSG_BOX_BASIC_STYLE, gzMPHScreenText[MPH_FILE_TRANSFER_DIR_NOT_EXIST], MP_HOST_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } +#endif + //} + return true; } UINT32 MPHostScreenInit( void ) { + // this is wrong as ScreenInit is called at game start, but the settigs can change anytime + // has be called in ScreenEnter for example + std::vector szTime(6,0); +#ifndef USE_VFS // read settings from JA2_mp.ini - GetPrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_NAME", L"Mr Server", gzServerNameField, 30, L"..\\Ja2_mp.ini" ); - GetPrivateProfileStringW( L"Ja2_mp Settings",L"MAX_CLIENTS", L"", gzMaxPlayersField, 2, L"..\\Ja2_mp.ini" ); - GetPrivateProfileStringW( L"Ja2_mp Settings",L"MAX_MERCS", L"", gzSquadSizeField, 2 , L"..\\Ja2_mp.ini" ); - guiMPHGameType = GetPrivateProfileIntW( L"Ja2_mp Settings",L"GAME_MODE", MP_TYPE_DEATHMATCH, L"..\\Ja2_mp.ini" ); - GetPrivateProfileStringW( L"Ja2_mp Settings",L"STARTING_BALANCE", L"9:30", gzStartingBalanceField, 10 , L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_NAME", L"Server Name", gzServerNameField, 30, L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"MAX_CLIENTS", L"4", gzMaxPlayersField, 4, L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"MAX_MERCS", L"5", gzSquadSizeField, 5 , L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"STARTING_BALANCE", L"25000", gzStartingBalanceField, 10 , L"..\\Ja2_mp.ini" ); GetPrivateProfileStringW( L"Ja2_mp Settings",L"DAMAGE_MULTIPLIER", L"0.7", gzDmgMultiplierField, 5 , L"..\\Ja2_mp.ini" ); - GetPrivateProfileStringW( L"Ja2_mp Settings",L"TIMED_TURN_SECS_PER_TICK", L"100", gzTimerField, 5 , L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"TIMED_TURN_SECS_PER_TICK", L"25", gzTimerField, 5 , L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"FILE_TRANSFER_DIRECTORY", L"MULTIPLAYER/Servers/My Server", gzFileTransferDirectory, 100, L"..\\Ja2_mp.ini" ); + GetStringProperty( L"Ja2_mp Settings", L"KIT_BAG", L"[201,214,243]", kbag, 100, L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"TIME", L"13.50", &szTime[0], 5 , L"..\\Ja2_mp.ini" ); + giMPHOverrideMaxAI = GetPrivateProfileIntW( L"Ja2_mp Settings",L"OVERRIDE_MAX_AI", 0, L"..\\Ja2_mp.ini" ); giMPHRandomMercs = GetPrivateProfileIntW( L"Ja2_mp Settings",L"RANDOM_MERCS", 0, L"..\\Ja2_mp.ini" ); - giMPHSameMercs = GetPrivateProfileIntW( L"Ja2_mp Settings",L"SAME_MERC", 0, L"..\\Ja2_mp.ini" ); - giMPHReportMercs = GetPrivateProfileIntW( L"Ja2_mp Settings",L"REPORT_NAME", 0, L"..\\Ja2_mp.ini" ); + giMPHSameMercs = GetPrivateProfileIntW( L"Ja2_mp Settings",L"SAME_MERC", 1, L"..\\Ja2_mp.ini" ); + giMPHReportMercs = GetPrivateProfileIntW( L"Ja2_mp Settings",L"REPORT_NAME", 1, L"..\\Ja2_mp.ini" ); giMPHBobbyRays = GetPrivateProfileIntW( L"Ja2_mp Settings",L"DISABLE_BOBBY_RAYS", 0, L"..\\Ja2_mp.ini" ); giMPHRandomSpawn = GetPrivateProfileIntW( L"Ja2_mp Settings",L"RANDOM_EDGES", 0, L"..\\Ja2_mp.ini" ); giMPHEnableCivilians = GetPrivateProfileIntW( L"Ja2_mp Settings",L"CIV_ENABLED", 0, L"..\\Ja2_mp.ini" ); giMPHUseNIV = GetPrivateProfileIntW( L"Ja2_mp Settings",L"ALLOW_CUSTOM_NIV", 0, L"..\\Ja2_mp.ini" ); + giMPHSendFiles = GetPrivateProfileIntW( L"Ja2_mp Settings",L"SYNC_CLIENTS_MP_DIR", 1, L"..\\Ja2_mp.ini" ); + guiMPHGameType = GetPrivateProfileIntW( L"Ja2_mp Settings",L"GAME_MODE", MP_TYPE_DEATHMATCH, L"..\\Ja2_mp.ini" ); + giMPHDifficultLevel = GetPrivateProfileIntW( L"Ja2_mp Settings",L"DIFFICULT_LEVEL", 2, L"..\\Ja2_mp.ini" ); +#else + // read settings from JA2_mp.ini + CPropertyContainer props; + props.InitFromIniFile("Ja2_mp.ini"); + props.GetStringProperty( L"Ja2_mp Settings", L"SERVER_NAME", gzServerNameField, 30, L"Server Name"); + props.GetStringProperty( L"Ja2_mp Settings", L"MAX_CLIENTS", gzMaxPlayersField, 4, L"4"); + props.GetStringProperty( L"Ja2_mp Settings", L"MAX_MERCS", gzSquadSizeField, 5, L"5"); + props.GetStringProperty( L"Ja2_mp Settings", L"STARTING_BALANCE", gzStartingBalanceField, 10, L"25000"); + props.GetStringProperty( L"Ja2_mp Settings", L"DAMAGE_MULTIPLIER", gzDmgMultiplierField, 5, L"0.7"); + props.GetStringProperty( L"Ja2_mp Settings", L"TIMED_TURN_SECS_PER_TICK", gzTimerField, 5, L"25"); + props.GetStringProperty( L"Ja2_mp Settings", L"FILE_TRANSFER_DIRECTORY", gzFileTransferDirectory, 100, L"MULTIPLAYER/Servers/My Server"); + props.GetStringProperty( L"Ja2_mp Settings", L"KIT_BAG", gzKitBag, 100, L"[201,214,243]"); + props.GetStringProperty(L"Ja2_mp Settings", L"TIME", &szTime[0], 6, L"13.50"); - - // read in time - CHAR16 szTime[5]; - GetPrivateProfileStringW( L"Ja2_mp Settings",L"TIME", L"13.50", szTime, 5 , L"..\\Ja2_mp.ini" ); + giMPHOverrideMaxAI = props.GetIntProperty( L"Ja2_mp Settings",L"OVERRIDE_MAX_AI", 0); + giMPHRandomMercs = props.GetIntProperty( L"Ja2_mp Settings",L"RANDOM_MERCS", 0); + giMPHSameMercs = props.GetIntProperty( L"Ja2_mp Settings",L"SAME_MERC", 1); + giMPHReportMercs = props.GetIntProperty( L"Ja2_mp Settings",L"REPORT_NAME", 1); + giMPHBobbyRays = props.GetIntProperty( L"Ja2_mp Settings",L"DISABLE_BOBBY_RAYS", 0); + giMPHRandomSpawn = props.GetIntProperty( L"Ja2_mp Settings",L"RANDOM_EDGES", 0); + giMPHEnableCivilians = props.GetIntProperty( L"Ja2_mp Settings",L"CIV_ENABLED", 0); + giMPHUseNIV = props.GetIntProperty( L"Ja2_mp Settings",L"ALLOW_CUSTOM_NIV", 0); + giMPHSendFiles = props.GetIntProperty( L"Ja2_mp Settings",L"SYNC_CLIENTS_MP_DIR", 1); + guiMPHGameType = (UINT8)props.GetIntProperty( L"Ja2_mp Settings",L"GAME_MODE", MP_TYPE_DEATHMATCH); + guiMPHDifficultLevel = (UINT8)props.GetIntProperty( L"Ja2_mp Settings",L"DIFFICULT_LEVEL", 3); // Expert +#endif wchar_t* tok; bool bTimeOK = true; int hours = 0; int mins = 0; - tok = wcstok(szTime,L"."); + tok = wcstok(&szTime[0],L"."); if (tok != NULL) { @@ -575,6 +766,9 @@ UINT32 MPHostScreenHandle( void ) { // PauseGame(); + // need to reload ja2_mp.ini + MPHostScreenInit(); + EnterMPHScreen(); gfMPHScreenEntry = FALSE; gfMPHScreenExit = FALSE; @@ -774,6 +968,16 @@ BOOLEAN EnterMPHScreen() 5, INPUTTYPE_ASCII );//23 + //Add File transfer directory textbox + AddTextInputField( MPH_TXT_FILE_TRANSFER_DIR_X, + MPH_TXT_FILE_TRANSFER_DIR_Y, + MPH_TXT_FILE_TRANSFER_DIR_WIDTH, + MPH_TXT_FILE_TRANSFER_DIR_HEIGHT, + MSYS_PRIORITY_HIGH+2, + gzFileTransferDirectory, + 100, + INPUTTYPE_ASCII ); + SetActiveField( 0 ); // Playername textbox has focus @@ -783,7 +987,7 @@ BOOLEAN EnterMPHScreen() // usPosY = MPH_DIF_SETTINGS_Y - MPH_OFFSET_TO_TOGGLE_BOX_Y; - for( cnt=0; cntuiFlags |= BUTTON_CLICKED_ON; - - else if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_MEDIUM ) - ButtonList[ guiMPHDifficultySettingsToggles[ MPH_DIFF_MED ] ]->uiFlags |= BUTTON_CLICKED_ON; - - else if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_HARD ) + if( guiMPHDifficultLevel == DIF_LEVEL_HARD ) ButtonList[ guiMPHDifficultySettingsToggles[ MPH_DIFF_HARD ] ]->uiFlags |= BUTTON_CLICKED_ON; - - else if( gGameOptions.ubDifficultyLevel == DIF_LEVEL_INSANE ) + else if( guiMPHDifficultLevel == DIF_LEVEL_INSANE ) ButtonList[ guiMPHDifficultySettingsToggles[ MPH_DIFF_INSANE ] ]->uiFlags |= BUTTON_CLICKED_ON; - else - ButtonList[ guiMPHDifficultySettingsToggles[ MPH_DIFF_MED ] ]->uiFlags |= BUTTON_CLICKED_ON; - - + ButtonList[ guiMPHDifficultySettingsToggles[ MPH_DIFF_HARD ] ]->uiFlags |= BUTTON_CLICKED_ON; // @@ -831,6 +1025,12 @@ BOOLEAN EnterMPHScreen() // set default selection ButtonList[ guiMPHGameTypeToggles[ guiMPHGameType ] ]->uiFlags |= BUTTON_CLICKED_ON; + // Override Max AI + guiMPHOverrideMaxAIToggle = CreateCheckBoxButton( MPH_OVERRIDEMAXAI_X+MPH_OFFSET_TO_TOGGLE_BOX, MPH_OVERRIDEMAXAI_Y - MPH_OFFSET_TO_TOGGLE_BOX_Y, + "INTERFACE\\OptionsCheck.sti", MSYS_PRIORITY_HIGH+10, + BtnMPHOverrideMaxAICallback ); + if ( giMPHOverrideMaxAI ) + ButtonList[ guiMPHOverrideMaxAIToggle ]->uiFlags |= BUTTON_CLICKED_ON; // Random Mercs guiMPHRandomMercsToggle = CreateCheckBoxButton( MPH_RNDMERC_X+MPH_OFFSET_TO_TOGGLE_BOX, MPH_RNDMERC_Y - MPH_OFFSET_TO_TOGGLE_BOX_Y, @@ -884,6 +1084,14 @@ BOOLEAN EnterMPHScreen() ButtonList[ guiMPHUseNIVToggle ]->uiFlags |= BUTTON_CLICKED_ON; + // checkbox for send files to clients + guiMPHSendFiles = CreateCheckBoxButton( MPH_SEND_FILES_X+MPH_OFFSET_TO_TOGGLE_BOX, MPH_SEND_FILES_Y - MPH_OFFSET_TO_TOGGLE_BOX_Y, + "INTERFACE\\OptionsCheck.sti", MSYS_PRIORITY_HIGH+10, + BtnMPHSendFilesCallback ); + + if (giMPHSendFiles) + ButtonList[ guiMPHSendFiles ]->uiFlags |= BUTTON_CLICKED_ON; + //Reset the exit screen - screen the main game loop will call next iteration @@ -919,13 +1127,14 @@ BOOLEAN ExitMPHScreen() UnloadButtonImage( giMPHStartBtnImage ); //Check box to toggle Difficulty settings - for( cnt=0; cntGetProfileStack(); + + // remove Multiplayer profile if it exists + vfs::CVirtualProfile *pProf = PS->GetProfile("_MULTIPLAYER"); + if( pProf && (pProf == PS->TopProfile()) ) + { + THROWIFFALSE(PS->PopProfile(), "Could not remove old \"_MULTIPLAYER\" profile"); + // careful, pProf is not valid anymore + } + // create and initialize a new Multiplayer profile + pProf = new vfs::CVirtualProfile("_MULTIPLAYER",true); + PS->PushProfile(pProf); + if(!InitWriteProfile(*pProf, vfs::Path(gzFileTransferDirectory))) + { + THROWIFFALSE(PS->PopProfile(), L"Could not remove \"_MULTIPLAYER\" profile"); + THROWEXCEPTION(L"Directory exists, but Multiplayer profile could not be iniitalized"); + } +#endif gubMPHScreenHandler = MPH_START; } break; @@ -1142,8 +1372,35 @@ void BtnMPHStartCallback(GUI_BUTTON *btn,INT32 reason) if (ValidateMPSettings()) { - SaveMPSettings(); gubMPHScreenHandler = MPH_START; + SaveMPSettings(); // Update the Profiles/UserProfile/ja2_mp.ini +#ifdef USE_VFS + // "gzFileTransferDirectory" is the new multiplayer profile root + vfs::CProfileStack *PS = GetVFS()->GetProfileStack(); + + // remove Multiplayer profile if it exists + vfs::CVirtualProfile *pProf = PS->GetProfile("_MULTIPLAYER"); + if( pProf && (pProf == PS->TopProfile()) ) + { + THROWIFFALSE(PS->PopProfile(), "Could not remove old \"_MULTIPLAYER\" profile"); + // careful, pProf is not valid anymore + } + // create and initialize a new Multiplayer profile + pProf = new vfs::CVirtualProfile("_MULTIPLAYER",true); + PS->PushProfile(pProf); + if(!InitWriteProfile(*pProf, vfs::Path(gzFileTransferDirectory))) + { + THROWIFFALSE(PS->PopProfile(), L"Could not remove \"_MULTIPLAYER\" profile"); + THROWEXCEPTION(L"Directory exists, but Multiplayer profile could not be iniitalized"); + } +#endif + + // The difficult level has to be set there. This is the only value so far, because it is used for initialization! + gGameOptions.ubDifficultyLevel = GetMPHCurrentDifficultyButtonSetting(); + + // WANNE - MP: Reinit TableData folder and INIs + LoadExternalGameplayData(TABLEDATA_DIRECTORY); + InitDependingGameStyleOptions(); } InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); @@ -1177,7 +1434,7 @@ void BtnMPHDifficultyTogglesCallback( GUI_BUTTON *btn, INT32 reason ) { UINT8 cnt; - for( cnt=0; cntuiFlags &= ~BUTTON_CLICKED_ON; } @@ -1191,7 +1448,7 @@ void BtnMPHDifficultyTogglesCallback( GUI_BUTTON *btn, INT32 reason ) BOOLEAN fAnyChecked=FALSE; //if none of the other boxes are checked, do not uncheck this box - for( cnt=0; cntuiFlags & BUTTON_CLICKED_ON ) @@ -1253,7 +1510,7 @@ void RestoreMPHButtonBackGrounds() usPosY = MPH_DIF_SETTINGS_Y-MPH_OFFSET_TO_TOGGLE_BOX_Y; //Check box to toggle Difficulty settings - for( cnt=0; cntuiFlags & BUTTON_CLICKED_ON ) { - return( cnt ); + return( cnt + 3 ); // removed two difficulty options from MPH } } @@ -1296,7 +1555,7 @@ UINT8 GetMPHGameTypeButtonSetting() { UINT8 cnt; - for( cnt=0; cntuiFlags & BUTTON_CLICKED_ON ) { @@ -1393,6 +1652,21 @@ void DoneFadeInForExitMPHScreen( void ) SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR ); } +void BtnMPHOverrideMaxAICallback(GUI_BUTTON *btn,INT32 reason) +{ + if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + MSYS_GetBtnUserData( btn, 0 ); + + if( btn->uiFlags & BUTTON_CLICKED_ON ) + { + ButtonList[ guiMPHOverrideMaxAIToggle ]->uiFlags &= ~BUTTON_CLICKED_ON; + + //enable the current button + btn->uiFlags |= BUTTON_CLICKED_ON; + } + } +} void BtnMPHRandomMercCallback(GUI_BUTTON *btn,INT32 reason) { @@ -1490,6 +1764,22 @@ void BtnMPHUseNIVCallback(GUI_BUTTON *btn,INT32 reason) } } +void BtnMPHSendFilesCallback(GUI_BUTTON *btn,INT32 reason) +{ + if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) + { + MSYS_GetBtnUserData( btn, 0 ); + + if( btn->uiFlags & BUTTON_CLICKED_ON ) + { + ButtonList[ guiMPHSendFiles ]->uiFlags &= ~BUTTON_CLICKED_ON; + + //enable the current button + btn->uiFlags |= BUTTON_CLICKED_ON; + } + } +} + void BtnMPHCivsCallback(GUI_BUTTON *btn,INT32 reason) { if( reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) diff --git a/MPJoinScreen.cpp b/MPJoinScreen.cpp index 8362cbbc..1149f349 100644 --- a/MPJoinScreen.cpp +++ b/MPJoinScreen.cpp @@ -29,7 +29,14 @@ #include "connect.h" #include "network.h" // for client name #include "saveloadscreen.h" +#include "game init.h" +#include "VFS/vfs.h" +#include "VFS/vfs_init.h" +#include "VFS/PropertyContainer.h" +#include "VFS/iteratedir.h" + +#include "Random.h" //////////////////////////////////////////// // @@ -113,9 +120,52 @@ UINT32 gubMPJExitScreen = MP_JOIN_SCREEN; // The screen that is in control next UINT32 guiMPJMainBackGroundImage; // Wide-char strings that will hold the variables until they are transferred to the CHAR ascii fields -CHAR16 gzPlayerHandleField[ 30 ] = {0} ; -CHAR16 gzServerIPField[ 15 ] = {0} ; -CHAR16 gzServerPortField[ 5 ] = {0} ; +CHAR16 gzPlayerHandleField[ 10+1 ] = {0} ; +CHAR16 gzServerIPField[ 15+1 ] = {0} ; +CHAR16 gzServerPortField[ 5+1 ] = {0} ; + +// client sets this when joining +extern CHAR16 gzFileTransferDirectory[100]; + +#ifdef USE_VFS +utf8string const& CUniqueServerId::GetServerId(vfs::Path dir, CPropertyContainer* props) +{ + if(!props) + { + return _id; + } + utf8string key = L"\"" + dir().c_wcs() + L"\""; + utf8string id = props->GetStringProperty(L"SERVER",key); + if(id.empty()) + { + std::vector _rand(30,0); + int pos = 0; + for(int block = 0; block < 5; ++block) + { + for(int i=0; i<5; ++i) + { + int r = Random(36); + if(r < 10) + { + r += L'0'; + } + else + { + r += L'A' - 10; + } + _rand[pos++] = r; + } + _rand[pos++] = L'-'; + } + id.r_wcs().assign(&_rand[0],29); + } + _id = id; + props->SetStringProperty(L"SERVER",key,_id); + return _id; +} + +CUniqueServerId s_ServerId; +#endif //////////////////////////////////////////// // @@ -155,7 +205,6 @@ BOOLEAN ExitMPJScreen(); void HandleMPJScreen(); BOOLEAN RenderMPJScreen(); void GetMPJScreenUserInput(); -void SaveJoinSettings(); bool ValidateJoinSettings(bool bSkipServerAddress); BOOLEAN DoMPJMessageBox( UINT8 ubStyle, const STR16 zString, UINT32 uiExitScreen, UINT16 usFlags, MSGBOX_CALLBACK ReturnCallback ); void DoneFadeOutForExitMPJScreen( void ); @@ -164,85 +213,137 @@ void DoneFadeInForExitMPJScreen( void ); UINT32 MPJoinScreenInit( void ) { +// WANNE - MP: Read the values from Profiles/UserProfile/ja2_mp.ini +#ifndef USE_VFS // read settings from JA2 ini - GetPrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_IP", L"", gzServerIPField, 15, L"..\\Ja2_mp.ini" ); - GetPrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_PORT", L"", gzServerPortField, 5, L"..\\Ja2_mp.ini" ); - GetPrivateProfileStringW( L"Ja2_mp Settings",L"CLIENT_NAME", L"Fresh Meat", gzPlayerHandleField, 30 , L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_IP", L"127.0.0.1", gzServerIPField, 16, L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_PORT", L"60005", gzServerPortField, 6, L"..\\Ja2_mp.ini" ); + GetPrivateProfileStringW( L"Ja2_mp Settings",L"CLIENT_NAME", L"Player Name", gzPlayerHandleField, 12 , L"..\\Ja2_mp.ini" ); +#else + CPropertyContainer props; + props.InitFromIniFile( L"Ja2_mp.ini"); + props.GetStringProperty( L"Ja2_mp Settings", L"SERVER_IP", gzServerIPField, 16, "127.0.0.1"); + props.GetStringProperty( L"Ja2_mp Settings", L"SERVER_PORT", gzServerPortField, 6, "60005"); + props.GetStringProperty( L"Ja2_mp Settings", L"CLIENT_NAME", gzPlayerHandleField, 12, L"Player Name"); +#endif return( 1 ); } -void SaveJoinSettings() +void SaveJoinSettings(bool ReSaving) { - Get16BitStringFromField( 0, gzPlayerHandleField ); // these indexes are based on the order created - Get16BitStringFromField( 1, gzServerIPField ); - Get16BitStringFromField( 2, gzServerPortField ); - + if (!ReSaving) + { + Get16BitStringFromField( 0, gzPlayerHandleField, 12 ); // these indexes are based on the order created + Get16BitStringFromField( 1, gzServerIPField, 16 ); + Get16BitStringFromField( 2, gzServerPortField, 6 ); + } + +#ifndef USE_VFS // save settings to JA2_mp.ini WritePrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_IP", gzServerIPField, L"..\\Ja2_mp.ini" ); WritePrivateProfileStringW( L"Ja2_mp Settings",L"SERVER_PORT", gzServerPortField, L"..\\Ja2_mp.ini" ); WritePrivateProfileStringW( L"Ja2_mp Settings",L"CLIENT_NAME", gzPlayerHandleField , L"..\\Ja2_mp.ini" ); +#else + CPropertyContainer props; + props.InitFromIniFile("Ja2_mp.ini"); + + props.SetStringProperty(L"Ja2_mp Settings",L"SERVER_IP", gzServerIPField); + props.SetStringProperty(L"Ja2_mp Settings",L"SERVER_PORT", gzServerPortField); + props.SetStringProperty(L"Ja2_mp Settings",L"CLIENT_NAME", gzPlayerHandleField); + + s_ServerId.GetServerId(vfs::Path(gzFileTransferDirectory), &props); + + props.WriteToIniFile(L"ja2_mp.ini",true); +#endif } -bool ValidateJoinSettings(bool bSkipServerAddress) +bool ValidateJoinSettings(bool bSkipServerAddress, bool bSkipSyncDir) { - // Check a Server name is entered - Get16BitStringFromField( 0, gzPlayerHandleField ); // these indexes are based on the order created + // Check a Player name is entered + Get16BitStringFromField( 0, gzPlayerHandleField, 12 ); // these indexes are based on the order created if (wcscmp(gzPlayerHandleField,L"")<=0) { DoMPJMessageBox( MSG_BOX_BASIC_STYLE, gzMPJScreenText[MPJ_HANDLE_INVALID], MP_JOIN_SCREEN, MSG_BOX_FLAG_OK, NULL ); return false; } - // dont check server address if we are going to HOST - if (bSkipServerAddress) - return true; - - // Verify the IP Address - Get16BitStringFromField( 1, gzServerIPField ); - - // loop through octets and check - int numOctets = 0; - wchar_t* tok; - tok = wcstok(gzServerIPField,L"."); - while (tok != NULL) + if (!bSkipServerAddress) { - numOctets++; - INT32 oct = _wtoi(tok); - // check for invalid conversion, ie alpha chars - // wtoi returns 0 if it cant convert, but we need this value - // therefore if tok <> 0 then it was a bad convert. - if (oct == 0 && wcscmp(tok,L"0") != 0) + // Verify the IP Address + Get16BitStringFromField( 1, gzServerIPField, 16 ); + + // loop through octets and check + int numOctets = 0; + wchar_t* tok; + tok = wcstok(gzServerIPField,L"."); + while (tok != NULL) { - // force error - numOctets=0; - break; + numOctets++; + INT32 oct = _wtoi(tok); + // check for invalid conversion, ie alpha chars + // wtoi returns 0 if it cant convert, but we need this value + // therefore if tok <> 0 then it was a bad convert. + if (oct == 0 && wcscmp(tok,L"0") != 0) + { + // force error + numOctets=0; + break; + } + + if (oct < 0 || oct > 254) // dont allow broadcast nums + { + // bad octet, error + numOctets=0; + break; + } + + // get next octet + tok = wcstok(NULL,L"."); } - if (oct < 0 || oct > 254) // dont allow broadcast nums + if (numOctets != 4) { - // bad octet, error - numOctets=0; - break; + // not a valid ip address + DoMPJMessageBox( MSG_BOX_BASIC_STYLE, gzMPJScreenText[MPJ_SERVERIP_INVALID], MP_JOIN_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } + + // Verify the Server Port + Get16BitStringFromField( 2, gzServerPortField, 6 ); + INT32 svrPort = _wtoi(gzServerPortField); + if (svrPort < 1 || svrPort > 65535) + { + DoMPJMessageBox( MSG_BOX_BASIC_STYLE, gzMPJScreenText[MPJ_SERVERPORT_INVALID], MP_JOIN_SCREEN, MSG_BOX_FLAG_OK, NULL ); + return false; + } + } + + if (!bSkipSyncDir) + { +#ifndef USE_VFS + // Create MULTIPLAYER / SERVERS if it dosent exist + STRING512 syncDir; + STRING512 executableDir; + + GetExecutableDirectory(executableDir); + sprintf(syncDir, "%s\\multiplayer", executableDir); + + if (!DirectoryExists(syncDir)) + { + CreateDirectoryA(syncDir, NULL); } - // get next octet - tok = wcstok(NULL,L"."); - } - - if (numOctets != 4) - { - // not a valid ip address - DoMPJMessageBox( MSG_BOX_BASIC_STYLE, gzMPJScreenText[MPJ_SERVERIP_INVALID], MP_JOIN_SCREEN, MSG_BOX_FLAG_OK, NULL ); - return false; - } - - // Verify the Server Port - Get16BitStringFromField( 2, gzServerPortField ); - INT32 svrPort = _wtoi(gzServerPortField); - if (svrPort < 1 || svrPort > 65535) - { - DoMPJMessageBox( MSG_BOX_BASIC_STYLE, gzMPJScreenText[MPJ_SERVERPORT_INVALID], MP_JOIN_SCREEN, MSG_BOX_FLAG_OK, NULL ); - return false; + sprintf(syncDir, "%s\\multiplayer\\servers", executableDir); + if (!DirectoryExists(syncDir)) + { + CreateDirectoryA(syncDir, NULL); + } +#else + if(os::CreateRealDirecory(vfs::Path(L"Multiplayer"))) + { + os::CreateRealDirecory(vfs::Path(L"Multiplayer/Servers")); + } +#endif } return true; @@ -402,7 +503,7 @@ BOOLEAN EnterMPJScreen() MPJ_TXT_HANDLE_HEIGHT, MSYS_PRIORITY_HIGH+2, gzPlayerHandleField, - 30, + 11, INPUTTYPE_ASCII );//23 //Add Server IP textbox @@ -577,10 +678,14 @@ void GetMPJScreenUserInput() break; case ENTER: - if (ValidateJoinSettings(false)) + if (ValidateJoinSettings(false, false)) { - SaveJoinSettings(); + SaveJoinSettings(false); gubMPJScreenHandler = MPJ_JOIN; + + // force client to use "MULTIPLAYER/SERVERS" path + memset(gzFileTransferDirectory,0,100*sizeof(CHAR16)); + wcscpy(gzFileTransferDirectory,L"multiplayer/servers"); } break; } @@ -602,11 +707,14 @@ void BtnMPJoinCallback(GUI_BUTTON *btn,INT32 reason) { btn->uiFlags &= (~BUTTON_CLICKED_ON ); - if (ValidateJoinSettings(false)) + if (ValidateJoinSettings(false, false)) { - SaveJoinSettings(); + SaveJoinSettings(false); gubMPJScreenHandler = MPJ_JOIN; - //gubMPJScreenHandler = MPJ_JOIN; + + // force client to use "MULTIPLAYER/SERVERS" path + memset(gzFileTransferDirectory,0,100*sizeof(CHAR16)); + wcscpy(gzFileTransferDirectory,L"multiplayer/servers"); } InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); @@ -624,9 +732,9 @@ void BtnMPJHostCallback(GUI_BUTTON *btn,INT32 reason) { btn->uiFlags &= (~BUTTON_CLICKED_ON ); - if (ValidateJoinSettings(true)) + if (ValidateJoinSettings(true, false)) { - SaveJoinSettings(); + SaveJoinSettings(false); gubMPJScreenHandler = MPJ_HOST; } InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); @@ -673,58 +781,19 @@ void DoneFadeOutForExitMPJScreen( void ) auto_retry = true; giNumTries = 5; - // loop through and get the status of all the buttons - // Madd - /*gGameOptions.fGunNut = GetCurrentGunButtonSetting(); - gGameOptions.ubGameStyle = GetCurrentGameStyleButtonSetting(); - gGameOptions.ubDifficultyLevel = GetCurrentDifficultyButtonSetting() + 1;*/ - // JA2Gold: no more timed turns setting - //gGameOptions.fTurnTimeLimit = GetCurrentTimedTurnsButtonSetting();//hayden : re-enabled + // set up and initialise a new game on the client + InitNewGame(false); + SetPendingNewScreen( MP_CONNECT_SCREEN ); if (is_networked) gGameOptions.fTurnTimeLimit = TRUE; else gGameOptions.fTurnTimeLimit = FALSE; - // JA2Gold: iron man - //gGameOptions.fIronManMode = GetCurrentGameSaveButtonSetting(); - // Bobby Rays - why would we want anything less than the best gGameOptions.ubBobbyRay = BR_AWESOME; - - // CHRISL: - /*if(IsNIVModeValid() == TRUE){ - switch ( GetCurrentINVOptionButtonSetting() ) - { - case GIO_INV_OLD: - gGameOptions.ubInventorySystem = INVENTORY_OLD; - break; - case GIO_INV_NEW: - gGameOptions.ubInventorySystem = INVENTORY_NEW; - break; - } - }*/ - - // gubGIOExitScreen = INIT_SCREEN; - gubMPJExitScreen = INTRO_SCREEN; - - //set the fact that we should do the intro videos -// gbIntroScreenMode = INTRO_BEGINING; -#ifdef JA2TESTVERSION - if( gfKeyState[ ALT ] ) - { - if( gfKeyState[ CTRL ] ) - { - gMercProfiles[ MIGUEL ].bMercStatus = MERC_IS_DEAD; - gMercProfiles[ SKYRIDER ].bMercStatus = MERC_IS_DEAD; - } - - SetIntroType( INTRO_ENDING ); - } - else -#endif - SetIntroType( INTRO_BEGINING ); + gubMPJExitScreen = MP_CONNECT_SCREEN; ExitMPJScreen(); // cleanup please, if we called a fadeout then we didnt do it above diff --git a/MPJoinScreen.h b/MPJoinScreen.h index b03b6c61..6d656e0c 100644 --- a/MPJoinScreen.h +++ b/MPJoinScreen.h @@ -1,12 +1,30 @@ #ifndef _MP_JOIN_SCREEN_H_ #define _MP_JOIN_SCREEN_H_ +#include "VFS/vfs.h" + +#ifdef USE_VFS + +class CPropertyContainer; + +class CUniqueServerId +{ +public: + utf8string const& GetServerId(vfs::Path dir, CPropertyContainer* props = NULL); +private: + utf8string _id; +}; + +extern CUniqueServerId s_ServerId; + +#endif UINT32 MPJoinScreenInit( void ); UINT32 MPJoinScreenHandle( void ); UINT32 MPJoinScreenShutdown( void ); +void SaveJoinSettings(bool ReSaving); -#endif \ No newline at end of file +#endif diff --git a/MPScoreScreen.cpp b/MPScoreScreen.cpp index 5b9499ba..9bcf7cf8 100644 --- a/MPScoreScreen.cpp +++ b/MPScoreScreen.cpp @@ -93,6 +93,7 @@ BOOLEAN gfMPSScreenEntry = TRUE; BOOLEAN gfMPSScreenExit = FALSE; BOOLEAN gfReRenderMPSScreen=TRUE; BOOLEAN gfMPSButtonsAllocated = FALSE; +BOOLEAN gfMPSScoreScreenCanContinue = FALSE; //enum for different states of screen enum @@ -222,6 +223,8 @@ UINT32 MPScoreScreenShutdown( void ) BOOLEAN EnterMPSScreen() { + gfMPSScoreScreenCanContinue = FALSE; + VOBJECT_DESC VObjectDesc; if( gfMPSButtonsAllocated ) @@ -326,6 +329,14 @@ void HandleMPSScreen() switch( gubMPSScreenHandler ) { case MPS_CANCEL: + // TODO.RW: Exit game, disconnect + + if (is_server) + server_disconnect(); + + if (is_client) + client_disconnect(); + gubMPSExitScreen = MAINMENU_SCREEN; gfMPSScreenExit = TRUE; break; @@ -357,6 +368,16 @@ void HandleMPSScreen() gubMPSScreenHandler = MPS_NOTHING; } + // disable the continue button + if (!is_server) + { + if (gfMPSScoreScreenCanContinue) + ShowButton( guiMPSContinueButton ); + else + HideButton( guiMPSContinueButton ); + } + + if( gfReRenderMPSScreen ) { @@ -492,6 +513,14 @@ BOOLEAN RenderMPSScreen() DisplayWrappedString( MPS_LABEL_ACCURACY_X, usPosY, MPS_LABEL_ACCURACY_WIDTH, 2, MPS_LABEL_TEXT_FONT, MPS_LABEL_TEXT_COLOR, szPlayerAccuracy, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); } + // OJW - 20090403 + // Draw Please wait for server message + if (!is_server) + { + if (!gfMPSScoreScreenCanContinue) + DisplayWrappedString( MPS_BTN_CONTINUE_X, MPS_BTN_CONTINUE_Y, 100, 2, MPS_LABEL_TEXT_FONT, MPS_LABEL_TEXT_COLOR, gzMPSScreenText[MPS_WAITSERVER_TEXT], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); + } + /* // Player name text label DisplayWrappedString( MPJ_LABEL_HANDLE_X, MPJ_LABEL_HANDLE_Y, MPJ_LABEL_HANDLE_WIDTH, 2, MPJ_LABEL_TEXT_FONT, MPJ_LABEL_TEXT_COLOR, gzMPJScreenText[ MPJ_HANDLE_TEXT ], FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); @@ -507,12 +536,9 @@ BOOLEAN RenderMPSScreen() -void GetMPSScreenUserInput() +void GetMPSScreenUserInput() { InputAtom Event; -// POINT MousePos; - -// GetCursorPos(&MousePos); while( DequeueEvent( &Event ) ) { @@ -527,12 +553,7 @@ void GetMPSScreenUserInput() gubMPSScreenHandler = MPS_CANCEL; break; - case ENTER: - /*if (ValidateJoinSettings(false)) - { - SaveJoinSettings(); - gubMPSScreenHandler = MPS_JOIN; - }*/ + case ENTER: gubMPSScreenHandler = MPS_CONTINUE; break; } diff --git a/MainMenuScreen.cpp b/MainMenuScreen.cpp index 391475c5..49c35781 100644 --- a/MainMenuScreen.cpp +++ b/MainMenuScreen.cpp @@ -38,6 +38,8 @@ #include "mercs.h" #include "gamesettings.h" #include "connect.h" +#include "VFS/vfs.h" +#include "VFS/vfs_profile.h" #define MAINMENU_TEXT_FILE "LoadScreens\\MainMenu.edt" #define MAINMENU_RECORD_SIZE 80 * 2 @@ -99,6 +101,8 @@ BOOLEAN CreateDestroyMainMenuButtons( BOOLEAN fCreate ); void RenderMainMenu(); void RestoreButtonBackGrounds(); +extern void InitSightRange(); //lal + @@ -281,16 +285,25 @@ BOOLEAN InitMainMenu( ) // gfDoHelpScreen = 0; - if(is_networked) - { - is_networked = FALSE; - // Snap: UN-Init MP save game directory - if ( !InitSaveDir() ) - { + if(is_networked) + { + is_networked = FALSE; +#ifdef USE_VFS + // remove Multiplayer profile if it exists + vfs::CProfileStack *PS = GetVFS()->GetProfileStack(); + vfs::CVirtualProfile *pProf = PS->GetProfile("_MULTIPLAYER"); + if( pProf && (pProf == PS->TopProfile()) ) + { + THROWIFFALSE(PS->PopProfile(), "Leaving Multiplayer mode : Could not remove \"_MULTIPLAYER\" profile"); + } +#endif - //if something didnt work, dont even know how to make error code...//hayden - } - } + // Snap: UN-Init MP save game directory + if ( !InitSaveDir() ) + { + //if something didnt work, dont even know how to make error code...//hayden + } + } //Check to see whatr saved game files exist InitSaveGameArray(); @@ -385,22 +398,27 @@ void ExitMainMenu( ) // WANNE - MP: This method initializes variables that should be initialized // differently for single and multiplayer -void InitDependingGameStyleOptions(BOOLEAN isNetworked) +void InitDependingGameStyleOptions() { - // Load the ja2_options.ini FreeGameExternalOptions(); + + // Load APBPConstants.ini + LoadGameAPBPConstants(); + // Load ja2_options.ini LoadGameExternalOptions(); + InitSightRange(); //lal ReStartingGame(); - InitGameOptions(); - if (isNetworked) + if (is_networked) { NUMBER_OF_MERCS = 28; LAST_MERC_ID = 27; } else { + InitGameOptions(); + NUMBER_OF_MERCS = 15; LAST_MERC_ID = 14; } @@ -428,6 +446,7 @@ void MenuButtonCallback(GUI_BUTTON *btn,INT32 reason) if(is_networked) { is_networked = FALSE; + giMAXIMUM_NUMBER_OF_PLAYER_SLOTS = CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; // Snap: UN-Init MP save game directory if ( !InitSaveDir() ) { @@ -442,6 +461,7 @@ void MenuButtonCallback(GUI_BUTTON *btn,INT32 reason) else if (gbHandledMainMenu == NEW_MP_GAME) { is_networked = TRUE; + giMAXIMUM_NUMBER_OF_PLAYER_SLOTS = 7; // Snap: Re-Init MP save game directory if ( !InitSaveDir() ) @@ -470,15 +490,8 @@ void MenuButtonCallback(GUI_BUTTON *btn,INT32 reason) if( gfKeyState[ ALT ] ) gfLoadGameUponEntry = TRUE; } - //else if ( gbHandledMainMenu == LOAD_MP_GAME ) - //{ - // is_networked = TRUE; - // if (gfKeyState[ ALT ] ) - // gfLoadGameUponEntry = TRUE; - //} - - InitDependingGameStyleOptions(is_networked); + InitDependingGameStyleOptions(); btn->uiFlags &= (~BUTTON_CLICKED_ON ); } diff --git a/MessageBoxScreen.cpp b/MessageBoxScreen.cpp index 93c84ff7..e5c16bde 100644 --- a/MessageBoxScreen.cpp +++ b/MessageBoxScreen.cpp @@ -1206,7 +1206,7 @@ UINT32 MessageBoxScreenHandle( ) else if( gMsgBox.usFlags & MSG_BOX_FLAG_INPUTBOX ) { // retrieve the string from the text box - Get16BitStringFromField( 0, gszMsgBoxInputString ); // these indexes are based on the order created + Get16BitStringFromField( 0, gszMsgBoxInputString, 255 ); // these indexes are based on the order created // Exit messagebox gMsgBox.bHandled = MSG_BOX_RETURN_OK; } diff --git a/Multiplayer/client.cpp b/Multiplayer/client.cpp index 3939bdeb..0e638013 100644 --- a/Multiplayer/client.cpp +++ b/Multiplayer/client.cpp @@ -76,6 +76,14 @@ #include "RakPeerInterface.h" #include "RakNetStatistics.h" #include "RakNetTypes.h" +#include "RakSleep.h" + +#include "FileListTransfer.h" +#include "FileListTransferCBInterface.h" +#include "FileOperations.h" +#include "SuperFastHash.h" +#include "RakAssert.h" +#include "IncrementalReadInterface.h" #include "BitStream.h" #include @@ -92,12 +100,14 @@ #include "tactical placement gui.h" #include "prebattle interface.h" -unsigned char GetPacketIdentifier(Packet *p); -unsigned char packetIdentifier; - #include "MessageBoxScreen.h" -#pragma pack(1) +#include "VFS/vfs.h" +#include "VFS/vfs_init.h" +#include "VFS/vfs_profile.h" +#include "VFS/vfs_file_raii.h" +#include "VFS/iteratedir.h" +#include "VFS/File/vfs_file.h" #include "keys.h" @@ -120,9 +130,307 @@ unsigned char packetIdentifier; #include "game init.h" #include "Debug Control.h" +#include "MPConnectScreen.h" + +#include "IniReader.h" + +extern CHAR16 gzFileTransferDirectory[100]; + +// WANNE: FILE TRANSFER +BOOLEAN fClientReceivedAllFiles = FALSE; +STRING512 client_executableDir; +STRING512 client_fileTransferDirectoryPath; // the clients file transfer directory absolut path +STRING512 server_fileTransferDirectoryPath; // the server file transfer directory absolut path +INT16 fileTransferProgress = 0; +INT16 serverSyncClientsDirectory = 0; + +unsigned char GetPacketIdentifier(Packet *p); +unsigned char packetIdentifier; + +// OJW - 20090405 +STRING512 gCurrentTransferFilename; +INT32 gCurrentTransferBytes = 0; +INT32 gTotalTransferBytes = 0; + extern INT8 SquadMovementGroups[ ]; RakPeerInterface *client; +// WANNE: FILE TRANSFER +FileListTransfer fltClient; // flt2 + +char *ReplaceCharactersInString_Client(char *str, char *orig, char *rep) +{ + static char buffer[4096]; + char *p; + + if(!(p = strstr(str, orig))) // Is 'orig' even in 'str'? + return str; + + strncpy(buffer, str, p-str); // Copy characters from 'str' start to 'orig' st$ + buffer[p-str] = '\0'; + + sprintf(buffer+(p-str), "%s%s", rep, p+strlen(orig)); + + return buffer; +} + +class ClientTransferCB : public FileListTransferCBInterface +{ + public: + // This method gets called for each file when it is completly received by the client. + // Now the file will be saved on the client + bool OnFile(OnFileStruct *onFileStruct) + { +#ifndef USE_VFS + // Get the directory path of the file and output it to the user! + char* targetFileName = ExtractFilename(onFileStruct->fileName); + //ScreenMsg( FONT_BCOLOR_ORANGE, MSG_MPSYSTEM, MPClientMessage[58], targetFileName); + //ScreenMsg( FONT_BCOLOR_ORANGE, MSG_MPSYSTEM, L"%i. (100%%) %i/%i %S %ib->%ib / %ib->%ib\n", onFileStruct->setID, onFileStruct->fileIndex+1, onFileStruct->setCount, onFileStruct->fileName, onFileStruct->compressedTransmissionLength, onFileStruct->finalDataLength, onFileStruct->setTotalCompressedTransmissionLength, onFileStruct->setTotalFinalLength); + + // Replace server executable directory with client executable directory then we have the correct path + char* tempFile = ReplaceCharactersInString_Client(onFileStruct->fileName, server_fileTransferDirectoryPath, client_fileTransferDirectoryPath); + char* pathWithoutFile = ReplaceCharactersInString_Client(tempFile, targetFileName, ""); + + // ---------------------------------- + // The directory does not exist, create it (and parent directories if needed) + // ---------------------------------- + if (!DirectoryExists(pathWithoutFile)) + { + // Remove the executable directory path + pathWithoutFile = ReplaceCharactersInString_Client(pathWithoutFile, client_executableDir, ""); + + wchar_t pathWithoutFileWChar[600]; + STRING512 currentDirectoryPath; + wchar_t* directoryName; + char directoryNameChar[100]; + + // Convert char* to wchar_t* + MultiByteToWideChar( CP_UTF8, 0, pathWithoutFile, -1, pathWithoutFileWChar, 600); + strcpy(currentDirectoryPath, client_executableDir); + + directoryName = wcstok(pathWithoutFileWChar,L"\\"); + while (directoryName != NULL) + { + // Convert to wchar_t* to char* + WideCharToMultiByte(CP_UTF8,0,directoryName,-1, directoryNameChar,100,NULL,NULL); + + // Get the full path of the directory + strcat(currentDirectoryPath, "\\"); + strcat(currentDirectoryPath, directoryNameChar); + + if (!DirectoryExists(currentDirectoryPath)) + { + // Folder does not exist, create it + if( !MakeFileManDirectory( currentDirectoryPath ) ) + AssertMsg( 0, String("Can't create new directory '%s'.", currentDirectoryPath) ); + } + + // get next directory (split on "\") + directoryName = wcstok(NULL,L"\\"); + } + } + + // OJW - 20090405 + char *relativeFname = ReplaceCharactersInString_Client(onFileStruct->fileName, server_fileTransferDirectoryPath,""); + strcpy(gCurrentTransferFilename,relativeFname); + SetConnectScreenSubMessageA(gCurrentTransferFilename); // setting this also causes connect screen to refresh + + // ---------------------------------- + // Save the file to clients harddisk + // ---------------------------------- + char *fileToSave = ReplaceCharactersInString_Client(onFileStruct->fileName, server_fileTransferDirectoryPath, client_fileTransferDirectoryPath); + + FILE *fp = fopen(fileToSave, "wb"); + fwrite(onFileStruct->fileData, onFileStruct->finalDataLength, 1, fp); + fclose(fp); +#else + if(!transferRules) + { + transferRules = new CTransferRules(); + transferRules->InitFromTxtFile("transfer_rules.txt"); + } + // Get the directory path of the file and output it to the user! + char* targetFileName = ExtractFilename(onFileStruct->fileName); + //ScreenMsg( FONT_BCOLOR_ORANGE, MSG_CHAT, MPClientMessage[58], targetFileName); + + vfs::Path fileName(onFileStruct->fileName); + utf8string::str_t const& valid_str = fileName().c_wcs(); + utf8string::size_t pos = valid_str.find(L":"); + if(pos != utf8string::str_t::npos) + { + // absolute path?? these are invalid and the server is not supposed to send us such paths + // potentialy malicious server -> output error + return false; + } + if(valid_str.substr(0,2) == vfs::Const::DOTDOT()) + { + // trying to break out from the profile? + // potentialy malicious server -> output error + return false; + } + if(transferRules && (transferRules->ApplyRule(valid_str) == CTransferRules::DENY)) + { + // sent file was on our ignore list + // it may be OK that the server's list and the clients' lists diverge + // send message to server that we cannot accept this file + return false; + } + + strcpy(gCurrentTransferFilename,onFileStruct->fileName); + + try + { + vfs::COpenWriteFile wfile(fileName,true,true); + vfs::UInt32 written=0; + wfile.file().Write(onFileStruct->fileData,onFileStruct->finalDataLength,written); + } + catch(CBasicException& ex) + { + ScreenMsg( FONT_BCOLOR_BLUE, MSG_CHAT, L"Could not write received file '%S'", targetFileName); + //RETHROWEXCEPTION(L"Could not write received file",ex); + } +#endif + + /* + // Make sure it worked + unsigned int hash1 = SuperFastHashFile(fileToSend); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash1, sizeof(hash1)); + unsigned int hash2 = SuperFastHashFile(targetFileName); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash2, sizeof(hash2)); + RakAssert(hash1==hash2); + */ + + //ScreenMsg( FONT_BCOLOR_ORANGE, MSG_MPSYSTEM, L"Saved file local in %S", file); + + // Return true to have RakNet delete the memory allocated to hold this file. + // False if you hold onto the memory, and plan to delete it yourself later + return true; + } + + // WANNE: FILE TRANSFER: This method gets called each periodically + virtual void OnFileProgress(OnFileStruct *onFileStruct,unsigned int partCount,unsigned int partTotal,unsigned int partLength, char *firstDataChunk) + { + static UINT32 iNextTransferProgressUpdateTime; + + gCurrentTransferBytes += (INT32)(onFileStruct->finalDataLength * (float)(1.0f/(float)partTotal)); + + if (guiBaseJA2NoPauseClock >= iNextTransferProgressUpdateTime) + { + char *relativeFname = ReplaceCharactersInString_Client(onFileStruct->fileName, server_fileTransferDirectoryPath,""); + strcpy(gCurrentTransferFilename,relativeFname); + + iNextTransferProgressUpdateTime = guiBaseJA2NoPauseClock + 100; + // update all clients of our file transfer progress + INT8 currentProgress = (INT8)(100.0f * (float)((float)gCurrentTransferBytes/(float)gTotalTransferBytes)); + + client_progress[CLIENT_NUM-1] = currentProgress; + client_downloading[CLIENT_NUM-1] = 1; + fDrawCharacterList = true; + + progress_struct prog; + prog.client_num = CLIENT_NUM; + prog.downloading = 1; + prog.progress = currentProgress; + + SetConnectScreenSubMessageA(gCurrentTransferFilename); // setting this also causes connect screen to refresh + + client->RPC("sendDOWNLOADSTATUS",(const char*)&prog, (int)sizeof(progress_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); + } + + /*INT16 currentProgress = 100 * partCount/partTotal; + INT16 fileTransferProgressNew = 0; + + // WANNE: FILE TRANSFER: Only output (0, 25, 50, 75) percentage + if (currentProgress < 25) + fileTransferProgressNew = 0; + else if (currentProgress < 50) + fileTransferProgressNew = 25; + else if (currentProgress < 75) + fileTransferProgressNew = 50; + else + fileTransferProgressNew = 75; + + if (fileTransferProgressNew != fileTransferProgress) + { + char* targetFileName = ExtractFilename(onFileStruct->fileName); + fileTransferProgress = fileTransferProgressNew; + ScreenMsg( FONT_BCOLOR_BROWN, MSG_MPSYSTEM, MPClientMessage[59], targetFileName, fileTransferProgress); + }*/ + + + //ScreenMsg( FONT_BLUE, MSG_MPSYSTEM, L"(%i%%) %S", 100*partCount/partTotal, onFileStruct->fileName); + //ScreenMsg( FONT_BLUE, MSG_MPSYSTEM, L"%i (%i%%) %i/%i %S %ib->%ib / %ib->%ib\n", onFileStruct->setID, 100*partCount/partTotal, onFileStruct->fileIndex+1, onFileStruct->setCount, onFileStruct->fileName, onFileStruct->compressedTransmissionLength, onFileStruct->finalDataLength, onFileStruct->setTotalCompressedTransmissionLength, onFileStruct->setTotalFinalLength, firstDataChunk); + + //printf("%i (%i%%) %i/%i %s %ib->%ib / %ib->%ib\n", onFileStruct->setID, 100*partCount/partTotal, onFileStruct->fileIndex+1, onFileStruct->setCount, onFileStruct->fileName, onFileStruct->compressedTransmissionLength, onFileStruct->finalDataLength, onFileStruct->setTotalCompressedTransmissionLength, onFileStruct->setTotalFinalLength, firstDataChunk); + } + + virtual bool OnDownloadComplete(void) + { + //printf("Download complete.\n"); + if (serverSyncClientsDirectory) + { + ScreenMsg( FONT_RED, MSG_MPSYSTEM, MPClientMessage[60]); + SetConnectScreenSubMessageW(MPClientMessage[60]); // setting this also causes connect screen to refresh + + //fileTransferProgress = 0; + + // notify ourselves + client_progress[CLIENT_NUM-1] = 0; + client_downloading[CLIENT_NUM-1] = 0; + // notify others + progress_struct prog; + prog.client_num = CLIENT_NUM; + prog.downloading = 0; // notify clients we have finished + prog.progress = 0; + + fDrawCharacterList = true; +#ifdef USE_VFS + if(transferRules) + { + delete transferRules; + transferRules = NULL; + } +#endif + client->RPC("sendDOWNLOADSTATUS",(const char*)&prog, (int)sizeof(progress_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); + } + + // Set a flag that we have received all the files. This is used that when another client connects we do not want to receive the files again! + fClientReceivedAllFiles = TRUE; + + // Returning false automatically deallocates the automatically allocated handler that was created by DirectoryDeltaTransfer + return false; + } + + private: + char *ExtractFilename(char *pathname) + { + char *s; + + if ((s=strrchr(pathname, '\\')) != NULL) s++; + else if ((s=strrchr(pathname, '/')) != NULL) s++; + else if ((s=strrchr(pathname, ':')) != NULL) s++; + else s = pathname; + return s; + } +#ifdef USE_VFS + // client has an ignore list too, as the server doesn't have to play by the rules + static CTransferRules* transferRules; +#endif +} transferCallback; + +#ifdef USE_VFS +CTransferRules* ClientTransferCB::transferRules = NULL; +#endif + +ClientTransferCB transferCBClient; + +// OJW - 20081222 +player_stats gMPPlayerStats[5]; + +// OJW - 20090503 - get rid of compile error +#pragma pack (1) typedef struct { @@ -204,7 +512,7 @@ typedef struct { UINT8 client_num; BOOLEAN bToAll; - CHAR16 msg[300]; + CHAR16 msg[512]; } chat_msg; bullets_table bTable[11][50]; @@ -215,9 +523,9 @@ int client_ready[4]; int client_edges[4]; int client_teams[4]; int random_mercs[7]; - -// OJW - 20081222 -player_stats gMPPlayerStats[5]; +// OJW - 20090305 +int client_downloading[4]; +int client_progress[4]; INT32 MAX_MERCS; @@ -239,7 +547,7 @@ UINT8 ubID_prefix; bool is_host=false; // OJW - added 20081130 - new flag to signal our intention to host, coming in from the HOST screen bool auto_retry=true; - int giNumTries = 5; // default is 5 retries + int giNumTries = MAX_CONNECT_RETRIES; // default is 5 retries UINT32 giNextRetryTime = 0; bool requested=false; @@ -251,6 +559,8 @@ bool DISABLE_MORALE; bool allowlaptop=false; bool recieved_settings=false; + // OJW - 20090422 + bool recieved_transfer_settings = false; bool getReal=false; @@ -266,6 +576,9 @@ char ckbag[100]; char SERVER_IP[30] ; char SERVER_PORT[30]; + char FILE_TRANSFER_DIRECTORY_CLIENT[100]; + + int SEND_FILES_CLIENT; int INVENTORY_MODE; int REPORT_NAME; @@ -307,6 +620,8 @@ void overide_callback( UINT8 ubResult ); void kick_callback( UINT8 ubResult ); void turn_callback (UINT8 ubResult); void ChatCallback (UINT8 ubResult); +void HandleClientConnectionLost(); +void disconnected_callback(UINT8 ubResult); // OJW - 20081222 void send_gameover( void ); @@ -325,6 +640,16 @@ UINT32 iCCStartGameTime = 0; // OJW - 20090317 bool is_game_started = false; +// OJW - 20090403 +extern BOOLEAN gfMPSScoreScreenCanContinue; // can the score screen continue + +// OJW - 20090430 +SystemAddress serverAddr; + +// OJW - 20090507 +settings_struct gMPServerSettings; // store a copy of our settings after we receive them +CHAR16 gszDisconnectReason[255]; // the reason we were disconnected from the server + // OJW - added 20081130 // - add retry timer and notification @@ -369,6 +694,19 @@ void NetworkAutoStart() } } +// OJW - this is just a fudge for now and needs to be improved later +bool are_clients_downloading() +{ + bool bDownloading = false; + for (int i=0; i < 4; i++) + { + if (client_downloading[i] == 1) + bDownloading = true; + } + + return bDownloading; +} + void HireRandomMercs() { MERC_HIRE_STRUCT HireMercStruct; @@ -401,12 +739,11 @@ void HireRandomMercs() //RPC sends and recieves: //******************** - void send_path ( SOLDIERTYPE *pSoldier, UINT16 sDestGridNo, UINT16 usMovementAnim, BOOLEAN fFromUI, BOOLEAN fForceRestartAnim ) { if(pSoldier->ubID < 120) { - //ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"Sending new path" ); + //ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"Sending new path" ); EV_S_SENDPATHTONETWORK SNetPath; @@ -443,7 +780,7 @@ void send_path ( SOLDIERTYPE *pSoldier, UINT16 sDestGridNo, UINT16 usMovementAn void recievePATH(RPCParameters *rpcParameters) { - //ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"Recieving new path," ); + //ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"Recieving new path," ); EV_S_SENDPATHTONETWORK* SNetPath = (EV_S_SENDPATHTONETWORK*)rpcParameters->input; @@ -501,7 +838,7 @@ void send_stance ( SOLDIERTYPE *pSoldier, UINT8 ubDesiredStance ) SChangeStance.sXPos = pSoldier->sX; SChangeStance.sYPos = pSoldier->sY; SChangeStance.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue; - //ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"change stance: %d",ubDesiredStance ); + //ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"change stance: %d",ubDesiredStance ); client->RPC("sendSTANCE",(const char*)&SChangeStance, (int)sizeof(EV_S_CHANGESTANCE)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); } @@ -669,6 +1006,8 @@ void recieveHIT(RPCParameters *rpcParameters) } + + void send_hire( UINT8 iNewIndex, UINT8 ubCurrentSoldier, INT16 iTotalContractLength, BOOLEAN fCopyProfileItemsOver) { @@ -696,7 +1035,7 @@ void send_hire( UINT8 iNewIndex, UINT8 ubCurrentSoldier, INT16 iTotalContractLen } - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[44], pSoldier->name); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[44], pSoldier->name); AddCharacterToAnySquad( pSoldier ); @@ -806,11 +1145,11 @@ void recieveHIRE(RPCParameters *rpcParameters) if(REPORT_NAME==1) { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[5],MercCreateStruct.bTeam-5,client_names[MercCreateStruct.bTeam-6],pSoldier->name ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[5],MercCreateStruct.bTeam-5,client_names[MercCreateStruct.bTeam-6],pSoldier->name ); } else { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[6],MercCreateStruct.bTeam-5,client_names[MercCreateStruct.bTeam-6] ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[6],MercCreateStruct.bTeam-5,client_names[MercCreateStruct.bTeam-6] ); } @@ -977,7 +1316,7 @@ UINT8 numenemyLAN( UINT8 ubSectorX, UINT8 ubSectorY ) void send_AI( SOLDIERCREATE_STRUCT *pCreateStruct, UINT8 *pubID ) { - //ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"send_AI" ); + //ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"send_AI" ); //SOLDIERCREATE_STRUCT aaa = *pCreateStruct; @@ -1105,7 +1444,7 @@ void send_ready ( void ) info.status = 1; status=1; numready = numready+1; - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[7],numready,MAX_CLIENTS ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[7],numready,MAX_CLIENTS ); // OJW - 20081204 client_ready[info.client_num-1]=1; fDrawCharacterList = true; // set the character list to be redrawn @@ -1115,7 +1454,7 @@ void send_ready ( void ) info.status = 0; status=0; numready = numready-1; - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[8],numready,MAX_CLIENTS ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[8],numready,MAX_CLIENTS ); // OJW - 20081204 client_ready[info.client_num-1]=0; fDrawCharacterList = true; // set the character list to be redrawn @@ -1126,7 +1465,7 @@ void send_ready ( void ) if(is_server && numready == MAX_CLIENTS) //all ready. and server tells all to load... { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[9] ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[9] ); goahead=1; readystage=1; @@ -1155,8 +1494,8 @@ void recieveREADY (RPCParameters *rpcParameters) if(info->ready_stage==1)//recived ok for go ahead from server for level load { numready++; - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[10], info->client_num,client_names[info->client_num-1],numready,MAX_CLIENTS ); - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[9] ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[10], info->client_num,client_names[info->client_num-1],numready,MAX_CLIENTS ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[9] ); status=0;//reset numready=0; goahead=1; @@ -1167,7 +1506,7 @@ void recieveREADY (RPCParameters *rpcParameters) if (info->status==1) { numready = numready+1; - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[10], info->client_num,client_names[info->client_num-1],numready,MAX_CLIENTS ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[10], info->client_num,client_names[info->client_num-1],numready,MAX_CLIENTS ); // OJW - 20081204 client_ready[info->client_num-1]=1; fDrawCharacterList = true; // set the character list to be redrawn @@ -1175,7 +1514,7 @@ void recieveREADY (RPCParameters *rpcParameters) else { numready = numready-1; - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[11], info->client_num,client_names[info->client_num-1],numready,MAX_CLIENTS ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[11], info->client_num,client_names[info->client_num-1],numready,MAX_CLIENTS ); // OJW - 20081204 client_ready[info->client_num-1]=0; fDrawCharacterList = true; // set the character list to be redrawn @@ -1192,7 +1531,7 @@ void recieveREADY (RPCParameters *rpcParameters) } else if(info->ready_stage==36)//server allows laptop access { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[36] ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[36] ); allowlaptop=1; if (RANDOM_MERCS) HireRandomMercs(); @@ -1260,7 +1599,7 @@ void send_donegui ( UINT8 ubResult ) info.status=1; gMsgBox.bHandled = MSG_BOX_RETURN_OK; KillTacticalPlacementGUI(); //send and kill - ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPClientMessage[13]); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPClientMessage[13]); } } else if(status==1)//was ready @@ -1322,7 +1661,7 @@ void recieveGUI (RPCParameters *rpcParameters) gMsgBox.bHandled = MSG_BOX_RETURN_OK; status=0; KillTacticalPlacementGUI(); - ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPClientMessage[13]); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPClientMessage[13]); client->RPC("sendGUI",(const char*)&info, (int)sizeof(ready_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); } @@ -1338,7 +1677,7 @@ void recieveGUI (RPCParameters *rpcParameters) { gMsgBox.bHandled = MSG_BOX_RETURN_OK; KillTacticalPlacementGUI(); - ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPClientMessage[13]); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPClientMessage[13]); numready=0; status=0; } @@ -1349,7 +1688,7 @@ void allowlaptop_callback ( UINT8 ubResult ) if(ubResult==2) { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[36] ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[36] ); allowlaptop=1; ready_struct info; @@ -1388,7 +1727,7 @@ void start_battle ( void ) { if(!is_client) { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[14] ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[14] ); } else if(!allowlaptop && is_server) { @@ -1398,15 +1737,21 @@ void start_battle ( void ) if (client_names[i] != NULL && strcmp(client_names[i],"") != 0) iPlayersConnected++; - if (iPlayersConnected > 1) + + if (iPlayersConnected <= 1) { - SGPRect CenterRect = { 100, 100, SCREEN_WIDTH - 100, 300 }; - DoMessageBox( MSG_BOX_BASIC_STYLE, MPClientMessage[35], guiCurrentScreen, MSG_BOX_FLAG_YESNO | MSG_BOX_FLAG_USE_CENTERING_RECT, allowlaptop_callback, &CenterRect ); + // notify the server that at least one other player must be connected + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[51] ); + } + else if (are_clients_downloading()) + { + // notify the server that some of the clients are still downloading + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[63] ); } else { - // notify the server that at least one other player must be connected - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[51] ); + SGPRect CenterRect = { 100, 100, SCREEN_WIDTH - 100, 300 }; + DoMessageBox( MSG_BOX_BASIC_STYLE, MPClientMessage[35], guiCurrentScreen, MSG_BOX_FLAG_YESNO | MSG_BOX_FLAG_USE_CENTERING_RECT, allowlaptop_callback, &CenterRect ); } } @@ -1415,7 +1760,7 @@ void start_battle ( void ) if ( NumberOfMercsOnPlayerTeam() ==0) { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[15] ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[15] ); } else if(goahead==1) { @@ -1445,13 +1790,14 @@ void start_battle ( void ) UINT32 i; for(i=0; i<4;i++) { - CHAR16 name[255]; + CHAR16 name[30]; int nm = mbstowcs( name, client_names[i], sizeof (char)*30 ); //copy in client specified name for the player turn bar :) if(nm) { // OJW - 20090318 - fixed name copying bug with multiple games CHAR16 full[255]; + memset(full,0,sizeof(CHAR16)*255); swprintf(full, MPClientMessage[57],i+1,name); memcpy( TeamTurnString[ (i+6) ] , full, sizeof( CHAR16) * 255 ); @@ -1480,7 +1826,7 @@ void start_battle ( void ) } else if(!allowlaptop && is_client && !is_server) { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[16] ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[16] ); } } @@ -1664,70 +2010,71 @@ void send_interrupt (SOLDIERTYPE *pSoldier) } +// WANNE - MP: Here we have to add AddTopMessage() on the clients void recieveINTERRUPT (RPCParameters *rpcParameters) { INT_STRUCT* INT = (INT_STRUCT*)rpcParameters->input; SOLDIERTYPE* pOpponent = MercPtrs[ INT->Interrupted]; if(INT->bTeam==netbTeam)// ||pOpponent->bTeam==1 )//for us or AI - { - INT->bTeam=0; - INT->ubID=INT->ubID - ubID_prefix; + { + INT->bTeam=0; + INT->ubID=INT->ubID - ubID_prefix; - for(int i=0; i <= INT->gubOutOfTurnPersons; i++) - { - if((INT->gubOutOfTurnOrder[i] >= ubID_prefix) && (INT->gubOutOfTurnOrder[i] < (ubID_prefix+5))) - { - INT->gubOutOfTurnOrder[i]=INT->gubOutOfTurnOrder[i]-ubID_prefix; - } - } - memcpy(gubOutOfTurnOrder,INT->gubOutOfTurnOrder, sizeof(UINT8) * MAXMERCS); - gubOutOfTurnPersons = INT->gubOutOfTurnPersons; - - } - - if( INT->bTeam != 0) + for(int i=0; i <= INT->gubOutOfTurnPersons; i++) + { + if((INT->gubOutOfTurnOrder[i] >= ubID_prefix) && (INT->gubOutOfTurnOrder[i] < (ubID_prefix+5))) { - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[17] ); - //stop moving merc who was interrupted and init UI bar - SOLDIERTYPE* pMerc = MercPtrs[ INT->ubID ]; - //AdjustNoAPToFinishMove( pMerc, TRUE ); - pMerc->HaultSoldierFromSighting(TRUE); - //pMerc->fTurningFromPronePosition = FALSE;// hmmm ?? - FreezeInterfaceForEnemyTurn(); - InitEnemyUIBar( 0, 0 ); - fInterfacePanelDirty = DIRTYLEVEL2; - AddTopMessage( COMPUTER_TURN_MESSAGE, TeamTurnString[ INT->bTeam ] ); - gTacticalStatus.fInterruptOccurred = TRUE; - + INT->gubOutOfTurnOrder[i]=INT->gubOutOfTurnOrder[i]-ubID_prefix; } - //else if ( pOpponent->bTeam == 1)// - //{ - // StartInterrupt(); - // ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"ai is interrupted" ); - //} + } + memcpy(gubOutOfTurnOrder,INT->gubOutOfTurnOrder, sizeof(UINT8) * MAXMERCS); + gubOutOfTurnPersons = INT->gubOutOfTurnPersons; + + // AI has interrupted + if (INT->bTeam == 1) + { + AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, TeamTurnString[ INT->bTeam ] ); + } else - { - //it for us ! :) - if(INT->gubOutOfTurnPersons==0)//indicates finished interrupt maybe can just call end interrupt - { - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"old int finish" ); - //prob never get here again, and can be removed later - - } - else //start our interrupt turn - { - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[37] ); - - SOLDIERTYPE* pSoldier = MercPtrs[ INT->ubID ]; - //SOLDIERTYPE* pOpponent = MercPtrs[ INT->Interrupted]; - ManSeesMan(pSoldier,pOpponent,pOpponent->sGridNo,pOpponent->pathing.bLevel,2,1); - StartInterrupt(); - } - } + { + AddTopMessage( PLAYER_INTERRUPT_MESSAGE, TeamTurnString[ INT->bTeam ] ); + } + } + // WANNE - MP: This seems to cause the HANG on AI interrupt where we have to press ALT + E on the server! + if( INT->bTeam != 0) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[17] ); + //stop moving merc who was interrupted and init UI bar + SOLDIERTYPE* pMerc = MercPtrs[ INT->ubID ]; + //AdjustNoAPToFinishMove( pMerc, TRUE ); + pMerc->HaultSoldierFromSighting(TRUE); + //pMerc->fTurningFromPronePosition = FALSE;// hmmm ?? + FreezeInterfaceForEnemyTurn(); + InitEnemyUIBar( 0, 0 ); + fInterfacePanelDirty = DIRTYLEVEL2; + AddTopMessage( COMPUTER_TURN_MESSAGE, TeamTurnString[ INT->bTeam ] ); + gTacticalStatus.fInterruptOccurred = TRUE; - //if(INT->gubOutOfTurnPersons > 0)ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Recieved Int: bTeam-%d ubID-%d", INT->bTeam,INT->ubID ); + } + else + { + //it for us ! :) + if(INT->gubOutOfTurnPersons==0)//indicates finished interrupt maybe can just call end interrupt + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"old int finish" ); + } + else //start our interrupt turn + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[37] ); + + SOLDIERTYPE* pSoldier = MercPtrs[ INT->ubID ]; + //SOLDIERTYPE* pOpponent = MercPtrs[ INT->Interrupted]; + ManSeesMan(pSoldier,pOpponent,pOpponent->sGridNo,pOpponent->pathing.bLevel,2,1); + StartInterrupt(); + } + } } void intAI (SOLDIERTYPE *pSoldier ) @@ -1808,7 +2155,10 @@ void resume_turn(RPCParameters *rpcParameters) } else if(INT->bTeam==1) { + // WANNE - MP: This happens, when client 1 (=server) has done its interrupt and now it is enemies turn! + // WANNE - MP: Removed the AddTopMessage() again ... ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"im not server but ai just got back its turn after being interrupted..." ); + AddTopMessage( COMPUTER_TURN_MESSAGE, TeamTurnString[ 1 ] ); //maybe can add something. } } @@ -1826,41 +2176,40 @@ void grid_display ( void ) //print mouse coordinates, helpfull for crate placeme ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[21], usMapPos ); } +// WANNE: No more needed void mp_help(void) { - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, L" "); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[2]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[3]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, L" "); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[4]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[5]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[6]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[7]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[8]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[9]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[10]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, L" "); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[11]); - - - + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, L" "); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[2]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[3]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, L" "); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[4]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[5]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[6]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[7]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[8]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[9]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[10]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, L" "); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[11]); } +// WANNE: No more needed void mp_help2(void) { - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, L" "); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[12]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, L" "); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[13]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, L" "); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[12]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, L" "); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[13]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[14]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[14]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[15]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[15]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, L" "); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[16]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[11]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, L" "); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[16]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[11]); } @@ -1951,7 +2300,7 @@ void overide_callback( UINT8 ubResult ) gMsgBox.bHandled = MSG_BOX_RETURN_OK; status=0; KillTacticalPlacementGUI(); //kill - ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPClientMessage[13]); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPClientMessage[13]); client->RPC("sendGUI",(const char*)&info, (int)sizeof(ready_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); @@ -1968,6 +2317,58 @@ void overide_callback( UINT8 ubResult ) } } +void requestFILE_TRANSFER_SETTINGS(void) +{ + client->RPC("requestFILE_TRANSFER_SETTINGS","", 0, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); +} + +// OJW - 20090430 +// give clients a choice to accept the security risk +void allowDownloadCallback( UINT8 ubResult ) +{ + if (ubResult == 2) + { + // yes + // begin downloading of files + setID = fltClient.SetupReceive(&transferCBClient, false, serverAddr); + + char buffer[3]; + sprintf(buffer, "%i", setID); + + client->RPC("receiveSETID", (const char*) buffer, (int)sizeof(char*), HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); + } + else + { + // no + // gracefully disconnect to the main menu + client_disconnect(); + guiPendingScreen = MP_JOIN_SCREEN; + } +} + +// THIS METHOD IS CALLED FROM THE SERVER WHENEVER A NEW CLIENT CONNECTS +void requestSETID(RPCParameters *rpcParameters) +{ + // WANNE: FILE TRANSFER + if (!is_server) + { + if (is_connected) // added this here for version disconnections + { + // We did not recieved the files, get them! + if (!fClientReceivedAllFiles) + { + serverAddr = rpcParameters->sender; + + SGPRect CenteringRect= {0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1 }; + DoMessageBox( MSG_BOX_BASIC_STYLE , MPClientMessage[64] , guiCurrentScreen, MSG_BOX_FLAG_YESNO | MSG_BOX_FLAG_USE_CENTERING_RECT, allowDownloadCallback, &CenteringRect ); + + } + } + } +} + + + void requestSETTINGS(void) { client_info cl_name; @@ -1976,14 +2377,125 @@ void requestSETTINGS(void) cl_name.team=TEAM; cl_name.cl_edge=atoi(SECT_EDGE); - + + // OJW - 20090507 + // send client version to server + strcpy(cl_name.client_version,MPVERSION); client->RPC("requestSETTINGS",(const char*)&cl_name, (int)sizeof(client_info)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); } +// OJW: FILE TRANSFER: Clients get notified of other clients transfer progress +void recieveDOWNLOADSTATUS(RPCParameters *rpcParameters) +{ + progress_struct* prog = (progress_struct*)rpcParameters->input; + int i = prog->client_num - 1; + + if (client_downloading[i] != prog->downloading) + { + if (prog->downloading == 0) + ScreenMsg( FONT_RED, MSG_MPSYSTEM, (is_server? MPServerMessage[11] : MPClientMessage[61]), client_names[i]); // message client has recieved all files + else + ScreenMsg( FONT_RED, MSG_MPSYSTEM, (is_server? MPServerMessage[12] : MPClientMessage[62]), client_names[i]);// send message client has started downloading files + } + + client_downloading[i] = prog->downloading; + client_progress[i] = prog->progress; + + fDrawCharacterList = true; +} +// WANNE: FILE TRANSFER: Get executable Directory from Server. This is used to get corret file location on client side +void recieveFILE_TRANSFER_SETTINGS (RPCParameters *rpcParameters) +{ + if (!is_server) + { + filetransfersettings_struct* fts = (filetransfersettings_struct*)rpcParameters->input; +#ifndef USE_VFS + // OJW - 20090405 + gCurrentTransferBytes = 0; + gTotalTransferBytes = fts->totalTransferBytes; + + // Now get directory + strcpy( server_fileTransferDirectoryPath, fts->fileTransferDirectory ); + + // Convert string to lowercase. This is needed, because the file path we received (see OnFile()) from list transfer is also lowercase + for (INT32 i = 0; server_fileTransferDirectoryPath[i]; ++i) + { + server_fileTransferDirectoryPath[i] = tolower(server_fileTransferDirectoryPath[i]); + putchar(server_fileTransferDirectoryPath[i]); + } + + recieved_transfer_settings = 1; + serverSyncClientsDirectory = fts->syncClientsDirectory; + + // We sync our MP game dir from the server + if (fts->syncClientsDirectory == 1) + { + strcat(client_fileTransferDirectoryPath, "\\"); + strcat(client_fileTransferDirectoryPath, fts->serverName); + + // Replace "/" with "\" + char* client_correctFileTransferDirectoryPath = ReplaceCharactersInString_Client(client_fileTransferDirectoryPath, "/", "\\"); + + strcpy(client_fileTransferDirectoryPath, client_correctFileTransferDirectoryPath); + + // Delete the clients mp directory. It will be recreated later when we receive the files from the server + if (DirectoryExists(client_fileTransferDirectoryPath)) + RemoveFileManDirectory(client_fileTransferDirectoryPath, true); + } +#else + gCurrentTransferBytes = 0; + gTotalTransferBytes = fts->totalTransferBytes; + + // Now get directory + strcpy( server_fileTransferDirectoryPath, fts->fileTransferDirectory ); + vfs::Path profileRoot = vfs::Path(gzFileTransferDirectory) + vfs::Path(server_fileTransferDirectoryPath); + + ///////////////////////////////////////////////////////////////////// + vfs::CProfileStack *PS = GetVFS()->GetProfileStack(); + + // remove Multiplayer profile if it exists + vfs::CVirtualProfile *pProf = PS->GetProfile("_MULTIPLAYER"); + if( pProf && (pProf == PS->TopProfile()) ) + { + THROWIFFALSE(PS->PopProfile(), L"Could not remove old \"_MULTIPLAYER\" profile"); + // careful, pProf is not valid anymore + } + // create and initialize a new Multiplayer profile + pProf = new vfs::CVirtualProfile("_MULTIPLAYER",true); + PS->PushProfile(pProf); + if(!InitWriteProfile(*pProf,profileRoot)) + { + // OK, directory did not exist, probably a new server + if(!os::CreateRealDirecory(profileRoot)) + { + THROWIFFALSE(PS->PopProfile(), L"Could not remove \"_MULTIPLAYER\" profile"); + THROWEXCEPTION(L"could not create client directory"); + } + // OK, try again + if(!InitWriteProfile(*pProf,profileRoot)) + { + THROWIFFALSE(PS->PopProfile(), L"Could not remove \"_MULTIPLAYER\" profile"); + THROWEXCEPTION(L"Could not initialize client profile"); + } + } + ///////////////////////////////////////////////////////////////////// + + + recieved_transfer_settings = 1; + serverSyncClientsDirectory = fts->syncClientsDirectory; + + // We sync our MP game dir from the server + if (fts->syncClientsDirectory == 1) + { + // profile is setup, nothing to do here + } +#endif + } +} void recieveSETTINGS (RPCParameters *rpcParameters) //recive settings from server { @@ -2003,6 +2515,9 @@ void recieveSETTINGS (RPCParameters *rpcParameters) //recive settings from serve // This settings packet contains information and settings specifically for us recieved_settings=1; + // save the settings so if we need to re-apply them after a reinitialisation ( after file transfer ) we can + memcpy ( &gMPServerSettings , cl_lan , sizeof(settings_struct) ); + CLIENT_NUM=cl_lan->client_num;//assign client number from server @@ -2030,7 +2545,7 @@ void recieveSETTINGS (RPCParameters *rpcParameters) //recive settings from serve memcpy(random_mercs,cl_lan->random_mercs,sizeof(int) * 7); } - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[2] ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[2] ); MAX_CLIENTS=cl_lan->max_clients; @@ -2124,8 +2639,8 @@ void recieveSETTINGS (RPCParameters *rpcParameters) //recive settings from serve TESTING=cl_lan->TESTING; REPORT_NAME=cl_lan->gsREPORT_NAME; - ScreenMsg( FONT_YELLOW, MSG_CHAT, MPClientMessage[24],str,MAX_CLIENTS,MAX_MERCS,PLAYER_BSIDE,SAME_MERC,DAMAGE_MULTIPLIER,gGameOptions.fTurnTimeLimit,secs_per_tick,cl_lan->soDis_Bobby,cl_lan->soDis_Equip,DISABLE_MORALE,TESTING); - if(TESTING) ScreenMsg( FONT_WHITE, MSG_CHAT, MPClientMessage[25] ); + ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, MPClientMessage[24],str,MAX_CLIENTS,MAX_MERCS,PLAYER_BSIDE,SAME_MERC,DAMAGE_MULTIPLIER,gGameOptions.fTurnTimeLimit,secs_per_tick,cl_lan->soDis_Bobby,cl_lan->soDis_Equip,DISABLE_MORALE,TESTING); + if(TESTING) ScreenMsg( FONT_WHITE, MSG_MPSYSTEM, MPClientMessage[25] ); // WANNE - MP: I just added the NUM_SEC_IN_DAY so the game starts at Day 1 instead of Day 0 gGameExternalOptions.iGameStartingTime= NUM_SEC_IN_DAY + int(cl_lan->TIME*3600); @@ -2133,15 +2648,30 @@ void recieveSETTINGS (RPCParameters *rpcParameters) //recive settings from serve // This also fixed the bug of the wrong hired hours! gGameExternalOptions.iFirstArrivalDelay = 0; + // Disable Reinforcements + gGameExternalOptions.gfAllowReinforcements = false; + gGameExternalOptions.gfAllowReinforcementsOnlyInCity = false; + + // Disable Real-Time Mode + gGameExternalOptions.fAllowRealTimeSneak = false; + gGameExternalOptions.fQuietRealTimeSneak = false; + // WANNE: fix HOT DAY in night at arrival by night. // Explanation: If game starting time + first arrival delay < 07:00 (111600) -> we arrive before the sun rises or // if game starting time + first arrival delay >= 21:00 (162000) -> we arrive after the sun goes down if( (gGameExternalOptions.iGameStartingTime + gGameExternalOptions. iFirstArrivalDelay) < 111600 || (gGameExternalOptions.iGameStartingTime + gGameExternalOptions.iFirstArrivalDelay >= 162000)) { + // Default: Night gubEnvLightValue = 12; - LightSetBaseLevel(gubEnvLightValue); - } + } + else + { + // Default: Day + gubEnvLightValue = 3; + } + + LightSetBaseLevel(gubEnvLightValue); InitNewGameClock( ); @@ -2174,7 +2704,7 @@ void recieveSETTINGS (RPCParameters *rpcParameters) //recive settings from serve // they depend on the inventory! LoadMercProfiles(); - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[26],cl_lan->client_num,szDefault ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[26],cl_lan->client_num,szDefault ); fDrawCharacterList = true; // set the character list to be redrawn @@ -2185,12 +2715,150 @@ void recieveSETTINGS (RPCParameters *rpcParameters) //recive settings from serve { fDrawCharacterList = true; // set the character list to be redrawn - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[26],cl_lan->client_num,szDefault ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[26],cl_lan->client_num,szDefault ); strcpy(client_names[cl_lan->client_num-1],szDefault); } +} +void reapplySETTINGS() +{ + // reapply some settings that would be lost if a game is re-initialised + // after joining ( from downloading files ) + + // Stuff from connect_client() + //********************** + //here some nifty little tweaks + LaptopSaveInfo.guiNumberOfMercPaymentsInDays += 20; + LaptopSaveInfo.gubLastMercIndex = LAST_MERC_ID; + + LaptopSaveInfo.ubLastMercAvailableId = 7; + gGameExternalOptions.fEnableSlayForever =1; + LaptopSaveInfo.gubPlayersMercAccountStatus = 4; + + extern BOOLEAN gfTemporaryDisablingOfLoadPendingFlag; + gfTemporaryDisablingOfLoadPendingFlag = TRUE; + SetBookMark( AIM_BOOKMARK ); + SetBookMark( BOBBYR_BOOKMARK ); + //SetBookMark( IMP_BOOKMARK ); + SetBookMark( MERC_BOOKMARK ); + + gMercProfiles[ 57 ].sSalary = 2000; + gMercProfiles[ 58 ].sSalary = 1500; + gMercProfiles[ 59 ].sSalary = 600; + gMercProfiles[ 60 ].sSalary = 500; + gMercProfiles[ 64 ].sSalary = 1500; + gMercProfiles[ 72 ].sSalary = 1000; + gMercProfiles[ 148 ].sSalary = 100; + gMercProfiles[ 68 ].sSalary = 2200; //iggy; + + // Stuff from recieveSETTINGS + gsMercArriveSectorX=gMPServerSettings.gsMercArriveSectorX; + gsMercArriveSectorY=gMPServerSettings.gsMercArriveSectorY; + + PLAYER_BSIDE=gMPServerSettings.gsPLAYER_BSIDE; + DISABLE_MORALE=gMPServerSettings.emorale; + ChangeSelectedMapSector( gsMercArriveSectorX, gsMercArriveSectorY, 0 ); + CHAR16 str[128]; + GetSectorIDString( gsMercArriveSectorX, gsMercArriveSectorY, 0, str, TRUE ); + //new --------- + gGameOptions.fTurnTimeLimit=gMPServerSettings.sofTurnTimeLimit; + INT32 secs_per_tick=gMPServerSettings.secs_per_tick; + PLAYER_TEAM_TIMER_SEC_PER_TICKS=secs_per_tick; + + INT32 clstarting_balance=gMPServerSettings.starting_balance;//set starting balance + + if(LaptopSaveInfo.iCurrentBalance we arrive before the sun rises or + // if game starting time + first arrival delay >= 21:00 (162000) -> we arrive after the sun goes down + if( (gGameExternalOptions.iGameStartingTime + gGameExternalOptions. iFirstArrivalDelay) < 111600 || + (gGameExternalOptions.iGameStartingTime + gGameExternalOptions.iFirstArrivalDelay >= 162000)) + { + // Default: Night + gubEnvLightValue = 12; + } + else + { + // Default: Day + gubEnvLightValue = 3; + } + + LightSetBaseLevel(gubEnvLightValue); + + InitNewGameClock( ); + + WEAPON_READIED_BONUS=gMPServerSettings.WEAPON_READIED_BONUS; + ALLOW_CUSTOM_NIV=gMPServerSettings.ALLOW_CUSTOM_NIV; + DISABLE_SPEC_MODE=gMPServerSettings.DISABLE_SPEC_MODE; + + // We have to take the selected inventory mode from the server + if(ALLOW_CUSTOM_NIV==0) + { + gGameOptions.ubInventorySystem=gMPServerSettings.sofNewInv; + } + + // WANNE - MP: We have to re-initialize the correct interface + if((UsingNewInventorySystem() == true) && IsNIVModeValid(true)) + { + InitNewInventorySystem(); + InitializeSMPanelCoordsNew(); + InitializeInvPanelCoordsNew(); + } + else + { + gGameOptions.ubInventorySystem = INVENTORY_OLD; + InitOldInventorySystem(); + InitializeSMPanelCoordsOld(); + InitializeInvPanelCoordsOld(); + } + + // WANNE - MP: We also have to reinitialize the merc profiles because + // they depend on the inventory! + LoadMercProfiles(); + + fDrawCharacterList = true; // set the character list to be redrawn } void recieveTEAMCHANGE( RPCParameters *rpcParameters ) @@ -2200,7 +2868,7 @@ void recieveTEAMCHANGE( RPCParameters *rpcParameters ) if (!can_teamchange()) { // error - ScreenMsg( FONT_YELLOW, MSG_CHAT, L"An error occured in recieveTEAMCHANGE that should not occur"); + ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"An error occured in recieveTEAMCHANGE that should not occur"); } else { @@ -2222,7 +2890,7 @@ void recieveEDGECHANGE( RPCParameters *rpcParameters ) if (!can_edgechange()) { // error - ScreenMsg( FONT_YELLOW, MSG_CHAT, L"An error occured in recieveEDGECHANGE that should not occur"); + ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"An error occured in recieveEDGECHANGE that should not occur"); } else { @@ -2246,7 +2914,7 @@ void recieveMAPCHANGE( RPCParameters *rpcParameters ) if (!is_client || allowlaptop) { // error - ScreenMsg( FONT_YELLOW, MSG_CHAT, L"An error occured in recieveMAPCHANGE that should not occur"); + ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"An error occured in recieveMAPCHANGE that should not occur"); } else { @@ -2263,18 +2931,19 @@ void recieveMAPCHANGE( RPCParameters *rpcParameters ) GetSectorIDString( gsMercArriveSectorX, gsMercArriveSectorY, 0, str, TRUE ); // notify clients of map change in console - ScreenMsg( FONT_YELLOW, MSG_CHAT, MPClientMessage[46],str); + ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, MPClientMessage[46],str); } } + void send_bullet( BULLET * pBullet,UINT16 usHandItem ) { netb_struct netb; netb.net_bullet=*pBullet; netb.usHandItem=usHandItem; - //ScreenMsg( FONT_YELLOW, MSG_CHAT, L"Sent Bullet Id: %d",pBullet->iBullet); + //ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"Sent Bullet Id: %d",pBullet->iBullet); if(pBullet->ubTargetID < 20)netb.net_bullet.ubTargetID = netb.net_bullet.ubTargetID+ubID_prefix; if(pBullet->ubFirerID < 20)netb.net_bullet.ubFirerID = netb.net_bullet.ubFirerID+ubID_prefix; @@ -2301,7 +2970,7 @@ void recieveBULLET(RPCParameters *rpcParameters) if (iBullet == -1) { - ScreenMsg( FONT_YELLOW, MSG_CHAT, L"Failed to create bullet"); + ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"Failed to create bullet"); } //add bullet to bullet table for translation @@ -2312,7 +2981,7 @@ void recieveBULLET(RPCParameters *rpcParameters) pBullet = GetBulletPtr( iBullet ); - //ScreenMsg( FONT_YELLOW, MSG_CHAT, L"Created Bullet Id: %d",iBullet); + //ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"Created Bullet Id: %d",iBullet); pBullet->fCheckForRoof=netb->net_bullet.fCheckForRoof; pBullet->qIncrX=netb->net_bullet.qIncrX; @@ -2347,7 +3016,7 @@ void send_changestate (EV_S_CHANGESTATE * SChangeState) client->RPC("sendSTATE",(const char*)&new_state, (int)sizeof(EV_S_CHANGESTATE)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); - //ScreenMsg( FONT_YELLOW, MSG_CHAT, L"sent state"); + //ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"sent state"); } @@ -2358,7 +3027,7 @@ void recieveSTATE(RPCParameters *rpcParameters) EV_S_CHANGESTATE* new_state = (EV_S_CHANGESTATE*)rpcParameters->input; - //ScreenMsg( FONT_YELLOW, MSG_CHAT, L"recieved state"); + //ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"recieved state"); SOLDIERTYPE * pSoldier=MercPtrs[ new_state->usSoldierID ]; @@ -2372,7 +3041,7 @@ void recieveSTATE(RPCParameters *rpcParameters) pSoldier->EVENT_SetSoldierDirection( new_state->usNewDirection ); } - //if(new_state->usNewState==95)ScreenMsg( FONT_YELLOW, MSG_CHAT, L"All Bandaged."); + //if(new_state->usNewState==95)ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"All Bandaged."); pSoldier->EVENT_InitNewSoldierAnim( new_state->usNewState, new_state->usStartingAniCode, new_state->fForce ); } @@ -2467,10 +3136,12 @@ void send_death( SOLDIERTYPE *pSoldier ) client->RPC("sendDEATH",(const char*)&nDeath, (int)sizeof(death_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); // print kill notice to screen - if (pSoldier->bTeam==1) ScreenMsg( FONT_YELLOW, MSG_CHAT, L"You Killed An Enemy AI"); - else ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[28],pS_name,(pS_bTeam),client_names[(pS_bTeam-1)],pA_name,(pA_bTeam),client_names[(pA_bTeam-1)] ); + if (pSoldier->bTeam==1) + ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, MPClientMessage[67]); + else + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[28],pS_name,(pS_bTeam),client_names[(pS_bTeam-1)],pA_name,(pA_bTeam),client_names[(pA_bTeam-1)] ); -#if _DEBUG +#ifdef JA2BETAVERSION char s_name[10]; char a_name[10]; WideCharToMultiByte(CP_UTF8,0,pS_name,-1, s_name,10,NULL,NULL); @@ -2479,7 +3150,7 @@ void send_death( SOLDIERTYPE *pSoldier ) else if (pAttacker->bTeam==1) MPDebugMsg( String ( "MPDEBUG SEND - '%s' (client %d - '%S') was killed by '%s' (client %d - '%s')\n",s_name,pS_bTeam,client_names[(pS_bTeam-1)],a_name,pA_bTeam,"Queens Army") ); else MPDebugMsg( String ( "MPDEBUG SEND - '%s' (client %d - '%S') was killed by '%s' (client %d - '%s')\n",s_name,pS_bTeam,client_names[(pS_bTeam-1)],a_name,pA_bTeam,client_names[(pA_bTeam-1)]) ); #endif - //ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[28],(pS_bTeam), pS_name,client_names[(pS_bTeam-1)],pA_name,(pA_bTeam),client_names[(pA_bTeam-1)] ); + //ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[28],(pS_bTeam), pS_name,client_names[(pS_bTeam-1)],pA_name,(pA_bTeam),client_names[(pA_bTeam-1)] ); } void recieveDEATH (RPCParameters *rpcParameters) @@ -2519,28 +3190,36 @@ void recieveDEATH (RPCParameters *rpcParameters) if(pSoldier->bActive) { pSoldier->usAnimState=50; - ScreenMsg( FONT_YELLOW, MSG_CHAT, L"made merc corpse/dead"); + + #ifdef JA2BETAVERSION + ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"made merc corpse/dead"); + #endif + RemoveManAsTarget(pSoldier); TurnSoldierIntoCorpse( pSoldier, TRUE, TRUE ); if ( CheckForEndOfBattle( FALSE ) ) { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"All over red rover..."); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"All over red rover..."); } - if (pSoldier->bTeam==1) ScreenMsg( FONT_YELLOW, MSG_CHAT, L"An Enemy AI was killed..."); - else ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[28],pS_name,(pS_bTeam),client_names[(pS_bTeam-1)],pA_name,(pA_bTeam),client_names[(pA_bTeam-1)] ); - //ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[28],(pS_bTeam),pS_name,client_names[(pS_bTeam-1)],pA_name,(pA_bTeam),client_names[(pA_bTeam-1)] ); + if (pSoldier->bTeam==1) + ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, MPClientMessage[67]); + else ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[28],pS_name,(pS_bTeam),client_names[(pS_bTeam-1)],pA_name,(pA_bTeam),client_names[(pA_bTeam-1)] ); + //ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[28],(pS_bTeam),pS_name,client_names[(pS_bTeam-1)],pA_name,(pA_bTeam),client_names[(pA_bTeam-1)] ); } else { - ScreenMsg( FONT_YELLOW, MSG_CHAT, L"merc allready corpse/dead"); - if (pSoldier->bTeam==1) ScreenMsg( FONT_YELLOW, MSG_CHAT, L"An Enemy AI was killed..."); + #ifdef JA2BETAVERSION + ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"merc already corpse/dead"); + #endif + + if (pSoldier->bTeam==1) ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, MPClientMessage[67]); CheckForEndOfBattle( FALSE ); } -#if _DEBUG +#ifdef JA2BETAVERSION char s_name[10]; char a_name[10]; WideCharToMultiByte(CP_UTF8,0,pS_name,-1, s_name,10,NULL,NULL); @@ -2549,14 +3228,13 @@ void recieveDEATH (RPCParameters *rpcParameters) else if (pAttacker->bTeam==1) MPDebugMsg( String ( "MPDEBUG RECV - '%s' (client %d - '%s') was killed by '%s' (client %d - '%s')\n",s_name,pS_bTeam,client_names[(pS_bTeam-1)],a_name,pA_bTeam,"Queens Army") ); else MPDebugMsg( String ( "MPDEBUG RECV - '%s' (client %d - '%s') was killed by '%s' (client %d - '%s')\n",s_name,pS_bTeam,client_names[(pS_bTeam-1)],a_name,pA_bTeam,client_names[(pA_bTeam-1)]) ); #endif - } void send_hitstruct(EV_S_STRUCTUREHIT * SStructureHit) { EV_S_STRUCTUREHIT struct_hit; memcpy( &struct_hit , SStructureHit, sizeof( EV_S_STRUCTUREHIT )); - if(SStructureHit->ubAttackerID <20)struct_hit.ubAttackerID = struct_hit.ubAttackerID+ubID_prefix; + if(SStructureHit->ubAttackerID <20)struct_hit.ubAttackerID = SStructureHit->ubAttackerID+ubID_prefix; client->RPC("sendhitSTRUCT",(const char*)&struct_hit, (int)sizeof(EV_S_STRUCTUREHIT)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); @@ -2567,7 +3245,7 @@ void send_hitwindow(EV_S_WINDOWHIT * SWindowHit) { EV_S_WINDOWHIT window_hit; memcpy( &window_hit , SWindowHit, sizeof( EV_S_WINDOWHIT )); - if(SWindowHit->ubAttackerID <20)window_hit.ubAttackerID = window_hit.ubAttackerID+ubID_prefix; + if(SWindowHit->ubAttackerID <20)window_hit.ubAttackerID = SWindowHit->ubAttackerID+ubID_prefix; client->RPC("sendhitWINDOW",(const char*)&window_hit, (int)sizeof(EV_S_WINDOWHIT)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); @@ -2578,7 +3256,7 @@ void send_miss(EV_S_MISS * SMiss) EV_S_MISS shot_miss; memcpy( &shot_miss , SMiss, sizeof( EV_S_MISS )); - if(SMiss->ubAttackerID <20)shot_miss.ubAttackerID = shot_miss.ubAttackerID+ubID_prefix; + if(SMiss->ubAttackerID <20)shot_miss.ubAttackerID = SMiss->ubAttackerID+ubID_prefix; client->RPC("sendMISS",(const char*)&shot_miss, (int)sizeof(EV_S_MISS)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); @@ -2587,16 +3265,16 @@ void send_miss(EV_S_MISS * SMiss) void recievehitSTRUCT (RPCParameters *rpcParameters) { EV_S_STRUCTUREHIT* struct_hit = (EV_S_STRUCTUREHIT*)rpcParameters->input; - //ScreenMsg( FONT_YELLOW, MSG_CHAT, L"recieved structure hit"); + //ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"recieved structure hit"); SOLDIERTYPE *pSoldier = MercPtrs[ struct_hit->ubAttackerID ]; INT8 bTeam=pSoldier->bTeam; INT32 iBullet = bTable[bTeam][struct_hit->iBullet].local_id; - if(struct_hit->fStopped)StopBullet( iBullet );//, ScreenMsg( FONT_YELLOW, MSG_CHAT, L"bullet stopped"); + if(struct_hit->fStopped)StopBullet( iBullet );//, ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"bullet stopped"); StructureHit( iBullet, struct_hit->usWeaponIndex, struct_hit->bWeaponStatus, struct_hit->ubAttackerID, struct_hit->sXPos, struct_hit->sYPos, struct_hit->sZPos, struct_hit->usStructureID, struct_hit->iImpact, struct_hit->fStopped ); - if(struct_hit->fStopped)RemoveBullet(iBullet);//, ScreenMsg( FONT_YELLOW, MSG_CHAT, L"bullet removed"); - //else ScreenMsg( FONT_YELLOW, MSG_CHAT, L"bullet left"); + if(struct_hit->fStopped)RemoveBullet(iBullet);//, ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"bullet removed"); + //else ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"bullet left"); } void recievehitWINDOW (RPCParameters *rpcParameters) @@ -2607,7 +3285,7 @@ void recievehitWINDOW (RPCParameters *rpcParameters) WindowHit( window_hit->sGridNo, window_hit->usStructureID, window_hit->fBlowWindowSouth, window_hit->fLargeForce ); - //ScreenMsg( FONT_YELLOW, MSG_CHAT, L"recieved window hit"); + //ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"recieved window hit"); } void recieveMISS (RPCParameters *rpcParameters) { @@ -2619,14 +3297,14 @@ void recieveMISS (RPCParameters *rpcParameters) ShotMiss( shot_miss->ubAttackerID, iBullet ); - //ScreenMsg( FONT_YELLOW, MSG_CHAT, L"recieved shot miss"); + //ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"recieved shot miss"); } void cheat_func(void) { if(TESTING) { - ScreenMsg( FONT_YELLOW, MSG_CHAT, L"gTacticalStatus.uiFlags |= SHOW_ALL_MERCS"); + ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"gTacticalStatus.uiFlags |= SHOW_ALL_MERCS"); gTacticalStatus.uiFlags |= SHOW_ALL_MERCS; } } @@ -2679,13 +3357,13 @@ BOOLEAN check_status (void)// any 'enemies' and clients left to fight ?? if( (gTacticalStatus.Team[ 0 ].bTeamActive == 0) && wiped==0)//server's team has been knocked out { wiped=1; - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[40] ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[40] ); if(!DISABLE_SPEC_MODE) { gTacticalStatus.uiFlags |= SHOW_ALL_MERCS;//hayden - ScreenMsg( FONT_YELLOW, MSG_CHAT, MPClientMessage[41] ); + ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, MPClientMessage[41] ); } - else ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"spectator mode disabled"); + else ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, L"spectator mode disabled"); teamwiped(); ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[42] ); @@ -2736,7 +3414,7 @@ void UpdateSoldierToNetwork ( SOLDIERTYPE *pSoldier ) if((time - (pSoldier->usLastUpdateTime)) > 2000 && pSoldier->stats.bLife!=0) { pSoldier->usLastUpdateTime = time; - //ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"update: %d ",time ); + //ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, L"update: %d ",time ); EV_S_UPDATENETWORKSOLDIER SUpdateNetworkSoldier; @@ -2786,18 +3464,18 @@ void UpdateSoldierFromNetwork (RPCParameters *rpcParameters) if( pSoldier->sGridNo != SUpdateNetworkSoldier->sAtGridNo) { pSoldier->EVENT_InternalSetSoldierPosition( sCellX, sCellY ,FALSE, FALSE, FALSE );//new syncing call to correct network lag/drift - //ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"sync ubid:%d grid %d to %d",pSoldier->ubID,pSoldier->sGridNo,SUpdateNetworkSoldier->sAtGridNo ); + //ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, L"sync ubid:%d grid %d to %d",pSoldier->ubID,pSoldier->sGridNo,SUpdateNetworkSoldier->sAtGridNo ); } if(pSoldier->ubDirection != SUpdateNetworkSoldier->ubDirection) { pSoldier->EVENT_SetSoldierDesiredDirection( SUpdateNetworkSoldier->ubDirection ); - //ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"sync ubid:%d dir %d to %d",pSoldier->ubID, pSoldier->ubDirection, SUpdateNetworkSoldier->ubDirection ); + //ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, L"sync ubid:%d dir %d to %d",pSoldier->ubID, pSoldier->ubDirection, SUpdateNetworkSoldier->ubDirection ); } if(gAnimControl[ pSoldier->usAnimState ].ubEndHeight != SUpdateNetworkSoldier->ubNewStance && pSoldier->bCollapsed != TRUE) { pSoldier->ChangeSoldierStance( SUpdateNetworkSoldier->ubNewStance ); - //ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"update stance"); + //ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, L"update stance"); } @@ -2967,13 +3645,13 @@ void recieveCHATMSG(RPCParameters* rpcParameters) if (client_teams[cmsg->client_num-1] == TEAM) { // only display on an ally client - ChatLogMessage( FONT_LTGREEN, MSG_CHAT, cmsg->msg); + ChatLogMessage( CHAT_FONT_COLOR, MSG_CHAT, cmsg->msg); } } else { // display to all clients - ChatLogMessage( FONT_LTGREEN, MSG_CHAT, cmsg->msg); + ChatLogMessage( CHAT_FONT_COLOR, MSG_CHAT, cmsg->msg); } } @@ -3035,6 +3713,17 @@ void recieveDISCONNECT(RPCParameters* rpcParameters) } +// OJW - 20090507 +// this function stores a reason from the server that we were disconnected +void recieveDISCONNECTREASON(RPCParameters *rpcParameters ) +{ + CHAR16* reason = (CHAR16*)rpcParameters->input; + wcscpy(gszDisconnectReason,reason); + + is_connected=false; + auto_retry = false; +} + void disconnected_callback(UINT8 ubResult) { if (iDisconnectedScreen == MAP_SCREEN) @@ -3062,6 +3751,13 @@ void disconnected_callback(UINT8 ubResult) // clean up all resources and exit from laptop to main menu SetPendingNewScreen(MAINMENU_SCREEN); // Laptop screen is always cleaned up on screen change in gameloop } + else if (iDisconnectedScreen == MP_CONNECT_SCREEN) + { + // Re-initialise the game + ReStartingGame(); + // else dont clean "everything" but still exit to main menu + SetPendingNewScreen(MP_JOIN_SCREEN); + } else { // Re-initialise the game @@ -3088,42 +3784,22 @@ void HandleClientConnectionLost() iDisconnectedScreen = guiCurrentScreen; SGPRect CenteringRect= {0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1 }; - UINT32 giMPHMessageBox = DoMessageBox( MSG_BOX_BASIC_STYLE, (recieved_settings ? MPClientMessage[48] : MPClientMessage[55] ), guiCurrentScreen, ( UINT16 ) ( MSG_BOX_FLAG_OK | MSG_BOX_FLAG_USE_CENTERING_RECT ),disconnected_callback, &CenteringRect ); - - } - - /* - // handle UI - if (guiCurrentScreen == MAP_SCREEN && !(gTacticalStatus.uiFlags & INCOMBAT)) - { - // in the map screen and not in combat - - - // refresh player list to remove from the game - fDrawCharacterList = true; // set the character list to be redrawn - fTeamPanelDirty = true; // redraw the background - // can set connection retries here if desired - - } - else if (guiCurrentScreen == GAME_SCREEN) // get a more valid check that the game is in progress here - { - // connection lost, let user know via popup then quit to main menu - SGPRect CenteringRect= {0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1 }; - UINT32 giMPHMessageBox = DoMessageBox( MSG_BOX_BASIC_STYLE, MPClientMessage[48], MAINMENU_SCREEN, ( UINT16 ) ( MSG_BOX_FLAG_OK | MSG_BOX_FLAG_USE_CENTERING_RECT ), NULL, &CenteringRect ); - - if (is_networked) + if (wcscmp(gszDisconnectReason,L"")==0) { - // haydent - if (is_server) - server_disconnect(); - - client_disconnect(); - + UINT32 giMPHMessageBox = DoMessageBox( MSG_BOX_BASIC_STYLE, (recieved_settings ? MPClientMessage[48] : MPClientMessage[55] ), guiCurrentScreen, ( UINT16 ) ( MSG_BOX_FLAG_OK | MSG_BOX_FLAG_USE_CENTERING_RECT ),disconnected_callback, &CenteringRect ); + } + else + { + UINT32 giMPHMessageBox = DoMessageBox( MSG_BOX_BASIC_STYLE, gszDisconnectReason, guiCurrentScreen, ( UINT16 ) ( MSG_BOX_FLAG_OK | MSG_BOX_FLAG_USE_CENTERING_RECT ),disconnected_callback, &CenteringRect ); } - //We want to reinitialize the game - ReStartingGame(); - }*/ + } + else + { + // Tell the score screen it can continue + gfMPSScoreScreenCanContinue = TRUE; + } + } void sendRT(void) @@ -3318,7 +3994,7 @@ void connect_client ( void ) if(!is_client) { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[0] ); + ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, MPClientMessage[0] ); @@ -3342,6 +4018,8 @@ void connect_client ( void ) REGISTER_STATIC_RPC(client, recieveREADY); REGISTER_STATIC_RPC(client, recieveGUI); REGISTER_STATIC_RPC(client, recieveSETTINGS); + REGISTER_STATIC_RPC(client, recieveDOWNLOADSTATUS); + REGISTER_STATIC_RPC(client, recieveFILE_TRANSFER_SETTINGS); REGISTER_STATIC_RPC(client, recieveTEAMCHANGE); REGISTER_STATIC_RPC(client, recieveEDGECHANGE); REGISTER_STATIC_RPC(client, recieveMAPCHANGE); @@ -3363,17 +4041,18 @@ void connect_client ( void ) REGISTER_STATIC_RPC(client, recieveGAMEOVER); REGISTER_STATIC_RPC(client, recieveDISCONNECT); REGISTER_STATIC_RPC(client, recieveCHATMSG); + REGISTER_STATIC_RPC(client, requestSETID); + REGISTER_STATIC_RPC(client, recieveDISCONNECTREASON); //*** if (b) { - //ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"Client started, waiting for connections..."); + //ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"Client started, waiting for connections..."); is_client=true; - /*repo=0;*/ } else { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"Client failed to start. Terminating."); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"Client failed to start. Terminating."); } @@ -3392,6 +4071,7 @@ void connect_client ( void ) recieved_settings=0; + recieved_transfer_settings=0; goahead = 0; numready = 0; readystage = 0; @@ -3408,6 +4088,9 @@ void connect_client ( void ) if (RANDOM_MERCS) memset (random_mercs,0,sizeof(int)*7); memset( gMPPlayerStats,0,sizeof(player_stats)*5); + memset ( &client_downloading,0,sizeof(int)*4); + memset ( &client_progress,0,sizeof(int)*4); + memset( &gszDisconnectReason,0,sizeof(CHAR16)*255); //retrieve settings from Ja2_mp.ini @@ -3425,41 +4108,65 @@ void connect_client ( void ) char clname[30]; - - MAX_CLIENTS=0;//reset server only set settings. //INTERRUPTS=0; DAMAGE_MULTIPLIER=0; SAME_MERC=0; DISABLE_SPEC_MODE=0; +#ifndef USE_VFS + CIniReader iniReader("..\\Ja2_mp.ini"); +#else + CIniReader iniReader("Ja2_mp.ini"); +#endif +#ifndef USE_VFS GetPrivateProfileString( "Ja2_mp Settings","SERVER_IP", "", ip, MAX_PATH, "..\\Ja2_mp.ini" ); GetPrivateProfileString( "Ja2_mp Settings","SERVER_PORT", "", port, MAX_PATH, "..\\Ja2_mp.ini" ); // GetPrivateProfileString( "Ja2_mp Settings","CLIENT_NUM", "", client_number, MAX_PATH, "..\\Ja2_mp.ini" ); GetPrivateProfileString( "Ja2_mp Settings","SECTOR_EDGE", "", sector_edge, MAX_PATH, "..\\Ja2_mp.ini" ); - +#else + strncpy(ip, iniReader.ReadString("Ja2_mp Settings","SERVER_IP", ""), 30); + strncpy(port, iniReader.ReadString("Ja2_mp Settings","SERVER_PORT", ""), 30); + // strcpy(client_number, iniReader.ReadString("Ja2_mp Settings","CLIENT_NUM", "")); + strncpy(sector_edge, iniReader.ReadString("Ja2_mp Settings","SECTOR_EDGE", ""), 30); +#endif //GetPrivateProfileString( "Ja2_mp Settings","CRATE_X", "", c_x, MAX_PATH, "..\\Ja2_mp.ini" ); - +#ifndef USE_VFS GetPrivateProfileString( "Ja2_mp Settings","CLIENT_NAME", "", clname, MAX_PATH, "..\\Ja2_mp.ini" ); + + GetPrivateProfileString( "Ja2_mp Settings","FILE_TRANSFER_DIRECTORY", "Data-MP", FILE_TRANSFER_DIRECTORY_CLIENT, MAX_PATH, "..\\Ja2_mp.ini" ); gGameOptions.ubInventorySystem = GetPrivateProfileInt( "Ja2_mp Settings","INVENTORY_MODE", INVENTORY_OLD, "..\\Ja2_mp.ini" ); +#else + strncpy(clname, iniReader.ReadString("Ja2_mp Settings","CLIENT_NAME", ""), 30); + + strncpy(FILE_TRANSFER_DIRECTORY_CLIENT, iniReader.ReadString("Ja2_mp Settings","FILE_TRANSFER_DIRECTORY", "Data-MP"), 100); + gGameOptions.ubInventorySystem = iniReader.ReadInteger("Ja2_mp Settings","INVENTORY_MODE", INVENTORY_OLD); +#endif char op1[30]; //char op2[30]; //char op3[30]; //char op4[30]; - - GetPrivateProfileString( "Ja2_mp Settings","TEAM", "", op1, MAX_PATH, "..\\Ja2_mp.ini" ); +#ifndef USE_VFS + GetPrivateProfileString( "Ja2_mp Settings","TEAM","0", op1, MAX_PATH, "..\\Ja2_mp.ini" ); //GetPrivateProfileString( "Ja2_mp Settings","OP_TEAM_2", "", op2, MAX_PATH, "..\\Ja2_mp.ini" ); //GetPrivateProfileString( "Ja2_mp Settings","OP_TEAM_3", "", op3, MAX_PATH, "..\\Ja2_mp.ini" ); //GetPrivateProfileString( "Ja2_mp Settings","OP_TEAM_4", "", op4, MAX_PATH, "..\\Ja2_mp.ini" ); /*char stt[30]; GetPrivateProfileString( "Ja2_mp Settings","START_TEAM_TURN", "", stt, MAX_PATH, "..\\Ja2_mp.ini" );*/ - +#else + strncpy(op1, iniReader.ReadString("Ja2_mp Settings","TEAM", ""), 30); + //GetPrivateProfileString( "Ja2_mp Settings","OP_TEAM_2", "", op2, MAX_PATH, "..\\Ja2_mp.ini" ); + //GetPrivateProfileString( "Ja2_mp Settings","OP_TEAM_3", "", op3, MAX_PATH, "..\\Ja2_mp.ini" ); + //GetPrivateProfileString( "Ja2_mp Settings","OP_TEAM_4", "", op4, MAX_PATH, "..\\Ja2_mp.ini" ); + /*char stt[30]; + GetPrivateProfileString( "Ja2_mp Settings","START_TEAM_TURN", "", stt, MAX_PATH, "..\\Ja2_mp.ini" );*/ +#endif TEAM=atoi(op1); //OP_TEAM_2=atoi(op2); @@ -3517,25 +4224,43 @@ void connect_client ( void ) //********************** + + // WANNE: FILE TRANSFER: Build the absolut file transfer directory path for the client + GetExecutableDirectory(client_executableDir); + + strcpy(client_fileTransferDirectoryPath, client_executableDir); + strcat(client_fileTransferDirectoryPath, "\\"); + strcat(client_fileTransferDirectoryPath, FILE_TRANSFER_DIRECTORY_CLIENT); + + // WANNE: FILE TRANSFER + fClientReceivedAllFiles = FALSE; + client->AttachPlugin(&fltClient); + client->SetSplitMessageProgressInterval(1); - - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[1],SERVER_IP); + CHAR16 tmpMessage[512]; + swprintf( tmpMessage, MPClientMessage[1],SERVER_IP ); + ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, tmpMessage); // we are connecting + SetConnectScreenSubMessageW( tmpMessage ); + + client->Connect(SERVER_IP, atoi(SERVER_PORT), 0,0); is_connecting=true; -#if _DEBUG +#ifdef JA2BETAVERSION MPDebugMsg( String ( "connect_client()\n" ) ); #endif + + } else if (is_connecting) { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[4] ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[4] ); } else if (is_connected) { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[3] ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[3] ); } } @@ -3557,13 +4282,13 @@ void client_packet ( void ) // We got a packet, get the identifier with our handy function packetIdentifier = GetPacketIdentifier(p); - //ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"packet recieved"); + //ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"packet recieved"); // Check if this is a network message packet switch (packetIdentifier) { case ID_DISCONNECTION_NOTIFICATION: // Connection lost normally - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_DISCONNECTION_NOTIFICATION"); + ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_DISCONNECTION_NOTIFICATION"); is_connected=false; //OJW - 20081223 //Gracefully notify and disconnect the client @@ -3574,39 +4299,49 @@ void client_packet ( void ) break; case ID_ALREADY_CONNECTED: // Connection lost normally - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_ALREADY_CONNECTED"); + ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_ALREADY_CONNECTED"); break; case ID_REMOTE_DISCONNECTION_NOTIFICATION: // Server telling the clients of another client disconnecting gracefully. You can manually broadcast this in a peer to peer enviroment if you want. - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_REMOTE_DISCONNECTION_NOTIFICATION"); + ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_REMOTE_DISCONNECTION_NOTIFICATION"); break; case ID_REMOTE_CONNECTION_LOST: // Server telling the clients of another client disconnecting forcefully. You can manually broadcast this in a peer to peer enviroment if you want. - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_REMOTE_CONNECTION_LOST"); + ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_REMOTE_CONNECTION_LOST"); break; case ID_REMOTE_NEW_INCOMING_CONNECTION: // Server telling the clients of another client connecting. You can manually broadcast this in a peer to peer enviroment if you want. - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_REMOTE_NEW_INCOMING_CONNECTION"); + ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_REMOTE_NEW_INCOMING_CONNECTION"); break; case ID_CONNECTION_ATTEMPT_FAILED: - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_CONNECTION_ATTEMPT_FAILED"); + ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_CONNECTION_ATTEMPT_FAILED"); is_connected=false; is_connecting=false; //OJW - 20081224 + CHAR16 msgString[512]; // handle auto retry if (auto_retry && giNumTries > 0) - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[49],giNumTries); // we already tried once, let the user know we are retrying + { + swprintf( msgString, MPClientMessage[49],giNumTries ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, msgString); // we already tried once, let the user know we are retrying + SetConnectScreenSubMessageW( msgString ); + } else - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[50],giNumTries); // we already tried once, let the user know we are retrying + { + swprintf( msgString, MPClientMessage[50],giNumTries ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, msgString); // we already tried once, let the user know we are retrying + SetConnectScreenSubMessageW( msgString ); + } giNextRetryTime = guiBaseJA2NoPauseClock + 5000; // 5 seconds? break; case ID_NO_FREE_INCOMING_CONNECTIONS: // Sorry, the server is full. I don't do anything here but // A real app should tell the user - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_NO_FREE_INCOMING_CONNECTIONS"); + ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_NO_FREE_INCOMING_CONNECTIONS"); break; case ID_CONNECTION_LOST: // Couldn't deliver a reliable packet - i.e. the other system was abnormally // terminated - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_CONNECTION_LOST"); + ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_CONNECTION_LOST"); + is_connected=false; //OJW - 20081223 //Gracefully notify and disconnect the client @@ -3616,27 +4351,29 @@ void client_packet ( void ) break; case ID_CONNECTION_REQUEST_ACCEPTED: // This tells the client they have connected - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_CONNECTION_REQUEST_ACCEPTED"); + ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_CONNECTION_REQUEST_ACCEPTED"); is_connected=true; is_connecting=false; + + // WANNE: FILE TRANSFER: Send all the data that is needed for the file transfer to the client, + // before the actual file transfer begins + requestFILE_TRANSFER_SETTINGS(); + requestSETTINGS(); - //request_settings();//ask server for game settings... break; - case ID_NEW_INCOMING_CONNECTION: + case ID_NEW_INCOMING_CONNECTION: //tells server client has connected - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_NEW_INCOMING_CONNECTION"); + ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_NEW_INCOMING_CONNECTION"); break; case ID_MODIFIED_PACKET: // Cheater! - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"ID_MODIFIED_PACKET"); + ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_MODIFIED_PACKET"); break; default: - - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"** a packet has been recieved for which i dont know what to do... **"); + ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"** a packet has been recieved for which i dont know what to do... **"); break; } - // We're done with the packet, get more :) client->DeallocatePacket(p); p = client->Receive(); @@ -3685,35 +4422,44 @@ void client_disconnect (void) { if(is_client) { - client->Shutdown(300); - is_client = false; - is_connected=false; - is_connecting=false; - - allowlaptop=false; + client->DetachPlugin(&fltClient); - // clear local client cache - memset(client_names,0,sizeof(char)*4*30); - memset(client_edges,0,sizeof(int)*4); - memset(client_ready,0,sizeof(int)*4); - memset(client_teams,0,sizeof(int)*4); - memset(gMPPlayerStats,0,sizeof(player_stats)*5); - memset(random_mercs,0,sizeof(int)*7); + client->Shutdown(300); + is_client = false; + is_connected=false; + is_connecting=false; + + fileTransferProgress = 0; - + allowlaptop=false; - // We're done with the network - RakNetworkFactory::DestroyRakPeerInterface(client); - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"client disconnected and shutdown"); -#if _DEBUG - MPDebugMsg( "client_disconnect()\n" ); -#endif + // clear local client cache + memset(client_names,0,sizeof(char)*4*30); + memset(client_edges,0,sizeof(int)*4); + memset(client_ready,0,sizeof(int)*4); + memset(client_teams,0,sizeof(int)*4); + memset(gMPPlayerStats,0,sizeof(player_stats)*5); + memset(random_mercs,0,sizeof(int)*7); + memset ( &client_downloading,0,sizeof(int)*4); + memset ( &client_progress,0,sizeof(int)*4); + TEAM=0; + + + + + // We're done with the network + RakNetworkFactory::DestroyRakPeerInterface(client); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"client disconnected and shutdown"); + + #ifdef JA2BETAVERSION + MPDebugMsg( "client_disconnect()\n" ); + #endif } else { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"client is not running"); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"client is not running"); } } @@ -3740,7 +4486,7 @@ void send_edgechange(int newedge) } else { - ScreenMsg( FONT_LTBLUE, MSG_CHAT, gszMPMapscreenText[3]); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, gszMPMapscreenText[3]); } } @@ -3771,7 +4517,7 @@ void send_teamchange(int newteam) } else { - ScreenMsg( FONT_LTBLUE, MSG_CHAT, gszMPMapscreenText[4]); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, gszMPMapscreenText[4]); } } @@ -3815,4 +4561,5 @@ void ChatCallback( UINT8 ubResult ) void OpenChatMsgBox( void ) { DoChatBox((guiCurrentScreen == GAME_SCREEN? true : false),gzMPChatboxText[1],guiCurrentScreen,ChatCallback,NULL); -} \ No newline at end of file +} + diff --git a/Multiplayer/connect.h b/Multiplayer/connect.h index 2f1e5a86..f8d4d3c8 100644 --- a/Multiplayer/connect.h +++ b/Multiplayer/connect.h @@ -12,6 +12,11 @@ extern bool is_server; extern bool is_networked; extern bool is_host; // OJW - added 20081129 +// OJW - 20040922 +extern bool recieved_settings; +extern bool recieved_transfer_settings; +extern INT16 serverSyncClientsDirectory; + extern int PLAYER_TEAM_TIMER_SEC_PER_TICKS; //extern char CLIENT_NUM[30]; @@ -39,6 +44,14 @@ extern int RANDOM_MERCS; //OJW - 20090317 extern bool is_game_started; +//OJW - 20090403 +extern int OVERRIDE_MAX_AI; + +//OJW - 20090405 +extern STRING512 gCurrentTransferFilename; +extern INT32 gCurrentTransferBytes; +extern INT32 gTotalTransferBytes; + extern UINT16 crate_usMapPos; //extern int INTERRUPTS; @@ -98,6 +111,8 @@ void send_interrupt(SOLDIERTYPE *pSoldier); void OpenChatMsgBox(void); +void reapplySETTINGS(); + BOOLEAN CheckConditionsForBattle( GROUP *pGroup ); // this comes from strategic movement.cpp extern char client_names[4][30]; @@ -108,10 +123,13 @@ extern char client_names[4][30]; extern int client_ready[4]; extern int client_teams[4]; extern int client_edges[4]; +extern int client_downloading[4]; +extern int client_progress[4]; extern char SERVER_NAME[30]; //OJW - 20081224 +#define MAX_CONNECT_RETRIES 5 extern bool auto_retry; extern int giNumTries; @@ -146,3 +164,11 @@ typedef struct extern player_stats gMPPlayerStats[5]; extern void game_over( void ); + +// OJW - 20090422 +extern BOOLEAN fClientReceivedAllFiles; + +// OJW - 20090507 +// Add basic version checking, will only work from now on +// note: this cannot be longer than char[30] +#define MPVERSION "1.13MP-v1.1" \ No newline at end of file diff --git a/Multiplayer/network.h b/Multiplayer/network.h index 48aa3cb8..088e5a1f 100644 --- a/Multiplayer/network.h +++ b/Multiplayer/network.h @@ -6,6 +6,10 @@ extern char CLIENT_NAME[30]; extern char SERVER_NAME[30]; extern bool Sawarded; +//extern char *fileToSend; +extern unsigned int setID; +//extern char *fileToSendCopy; + typedef struct { @@ -13,6 +17,7 @@ typedef struct char client_name[30]; int team; int cl_edge; + char client_version[30]; }client_info; typedef struct @@ -61,9 +66,18 @@ typedef struct int RANDOM_SPAWN; int RANDOM_MERCS; int random_mercs[7]; + char server_version[30]; } settings_struct; - +// WANNE: FILE TRANSFER +typedef struct +{ + STRING512 fileTransferDirectory; + //char fileTransferDirectory[600]; // The file transfer directory path from the server + int syncClientsDirectory; // Does the server want to sync files to the clients + char serverName[30]; // The name of the server. This is used on the client side as a folder inside the client transfer directory + long totalTransferBytes; +} filetransfersettings_struct; // added 080101 by OJW typedef struct @@ -85,6 +99,13 @@ typedef struct UINT8 newteam; } teamchange_struct; +typedef struct +{ + UINT8 client_num; + UINT8 progress; + UINT8 downloading; +} progress_struct; + //typedef struct //{ // int clnum; diff --git a/Multiplayer/server.cpp b/Multiplayer/server.cpp index 32bea4d3..add38ec2 100644 --- a/Multiplayer/server.cpp +++ b/Multiplayer/server.cpp @@ -5,6 +5,14 @@ #include "RakNetStatistics.h" #include "RakNetTypes.h" + +#include "FileListTransfer.h" +#include "FileListTransferCBInterface.h" +#include "FileOperations.h" +#include "SuperFastHash.h" +#include "RakAssert.h" +#include "IncrementalReadInterface.h" + #include "BitStream.h" #include "RakSleep.h" #include @@ -16,8 +24,60 @@ #include #include +#include "connect.h" + #include "types.h" #include "gamesettings.h" +#include "message.h" +#include "FileMan.h" + +#include "IniReader.h" +#include "VFS/vfs.h" +#include "MPJoinScreen.h" + +#include "game init.h" + +#include "text.h" +#include "network.h" + +#include "message.h" +#include "overhead.h" +#include "fresh_header.h" +#include "Debug Control.h" + +extern CHAR16 gzFileTransferDirectory[100]; + +// WANNE: FILE TRANSFER +unsigned int setID; + +// Sender progress notification +class ServerFileListProgress : public FileListProgress +{ + virtual void OnFilePush(const char *fileName, unsigned int fileLengthBytes, unsigned int offset, unsigned int bytesBeingSent, bool done, SystemAddress targetSystem) + { + // WANNE: Removed output in strategy log screen, because otherwise we do not see chat messages easily + /* + if (done) + { + char* filename = ExtractFilename((char*)fileName); + ScreenMsg( FONT_RED, MSG_MPSYSTEM, MPServerMessage[10], filename); + } + */ + } + + char *ExtractFilename(char *pathname) + { + char *s; + + if ((s=strrchr(pathname, '\\')) != NULL) s++; + else if ((s=strrchr(pathname, '/')) != NULL) s++; + else if ((s=strrchr(pathname, ':')) != NULL) s++; + else s = pathname; + return s; + } +} serverFileListProgress; + + char kbag[100]; char net_div[30]; @@ -25,15 +85,7 @@ INT32 gsMAX_MERCS; bool gsMORALE; int gsREPORT_NAME; -#include "game init.h" -#include "text.h" -#include "network.h" -#include "connect.h" -#include "message.h" -#include "overhead.h" -#include "fresh_header.h" -#include "Debug Control.h" UINT16 nc; //number of open connection UINT16 ncr; //number of ready confirmed connections //something keep record of ready connections .. @@ -48,6 +100,10 @@ int gsINTERRUPTS ; int gsMAX_CLIENTS ; int gsPLAYER_BSIDE; int gsTESTING; +int gsDIFFICULT_LEVEL; +char gsFILE_TRANSFER_DIRECTORY_SERVER[100]; + +int gsSYNC_CLIENTS_MP_DIR; INT32 gssecs_per_tick; INT32 gsstarting_balance; @@ -56,11 +112,25 @@ int sWEAPON_READIED_BONUS; int sALLOW_CUSTOM_NIV; int sDISABLE_SPEC_MODE; +// OJW - 20090403 - override max enemies for co-op +int OVERRIDE_MAX_AI; + unsigned char SGetPacketIdentifier(Packet *p); unsigned char SpacketIdentifier; RakPeerInterface *server; + + +// WANNE: FILE TRANSFER +FileListTransfer fltServer; // flt1 +IncrementalReadInterface incrementalReadInterface; +FileList fileList; +// OJW - 20090405 +long fileListTotalBytes=0; + + + int numreadyteams; int readyteamreg[10]; @@ -92,6 +162,22 @@ int client_mercteam[4] = { 0 , 1 , 2 , 3 }; // random index of random_merc_teams bool inline can_joingame(); +char *ReplaceCharactersInString_Server(char *str, char *orig, char *rep) +{ + static char buffer[4096]; + char *p; + + if(!(p = strstr(str, orig))) // Is 'orig' even in 'str'? + return str; + + strncpy(buffer, str, p-str); // Copy characters from 'str' start to 'orig' st$ + buffer[p-str] = '\0'; + + sprintf(buffer+(p-str), "%s%s", rep, p+strlen(orig)); + + return buffer; +} + int f_rec_num(int mode, SystemAddress sender)//from client data { int x; @@ -133,7 +219,7 @@ int f_rec_num(int mode, SystemAddress sender)//from client data } if(mode == 0)//'no free slots' { - ScreenMsg( FONT_RED, MSG_CHAT, L"Client Record Error, Restart Server, and Report Error." ); + ScreenMsg( FONT_RED, MSG_MPSYSTEM, L"Client Record Error, Restart Server, and Report Error." ); return (255); } return(254); @@ -148,6 +234,12 @@ void sendPATH(RPCParameters *rpcParameters) server->RPC("recievePATH",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } +// OJW - 20090405 +void sendDOWNLOADSTATUS(RPCParameters *rpcParameters) +{ + server->RPC("recieveDOWNLOADSTATUS",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); +} + void sendSTANCE(RPCParameters *rpcParameters) { server->RPC("recieveSTANCE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); @@ -168,9 +260,15 @@ void sendHIT(RPCParameters *rpcParameters) EV_S_WEAPONHIT* hit = (EV_S_WEAPONHIT*)rpcParameters->input; int team = MercPtrs[ hit->ubAttackerID ]->bTeam; - if (team == 1) team = 5; - else if (team >= 6) team -= 6; - else if (team == 0) team = CLIENT_NUM-1; // this case should not be possible, including as a precaution + + // AI + if (team == 1) + team = 4; + // Client + else if (team >= 6) + team -= 6; + else if (team == 0) + team = CLIENT_NUM-1; // this case should not be possible, including as a precaution gMPPlayerStats[team].hits++; @@ -233,25 +331,24 @@ void sendDEATH(RPCParameters *rpcParameters) { // the master copy of the scoreboard is kept on the server death_struct* nDeath = (death_struct*)rpcParameters->input; - + // Save Stats on the server side gMPPlayerStats[nDeath->soldier_team-1].deaths++; gMPPlayerStats[nDeath->attacker_team-1].kills++; - // get the client number of the client sending the message int iCLnum = f_rec_num(3,rpcParameters->sender)+1; server->RPC("recieveDEATH",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); -#if _DEBUG +#ifdef JA2BETAVERSION wchar_t ateam[5]; wchar_t steam[5]; wchar_t clnum[5]; _itow(nDeath->attacker_team,ateam,10); _itow(nDeath->soldier_team,steam,10); _itow(iCLnum,clnum,10); - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"DEBUG: Soldier Killed : Attacking team %s , Soldier Team %s, Sender %s",ateam,steam,clnum); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, L"DEBUG: Soldier Killed : Attacking team %s , Soldier Team %s, Sender %s",ateam,steam,clnum); char logmsg[100]; sprintf(logmsg, "MP DEBUG: Soldier Killed #%i : Attacking team %i , Soldier Team %i, Sender %i\n",nDeath->soldier_id,nDeath->attacker_team,nDeath->soldier_team,iCLnum); MPDebugMsg( logmsg ); @@ -261,12 +358,22 @@ void sendhitSTRUCT(RPCParameters *rpcParameters) { EV_S_STRUCTUREHIT* miss = (EV_S_STRUCTUREHIT*)rpcParameters->input; - int team = MercPtrs[ miss->ubAttackerID ]->bTeam; - if (team == 1) team = 5; - else if (team >= 6) team -= 6; - else if (team == 0) team = CLIENT_NUM-1; // this case should not be possible, including as a precaution + SOLDIERTYPE* pAttacker = MercPtrs[ miss->ubAttackerID ]; + if (pAttacker != NULL) + { + int team = MercPtrs[ miss->ubAttackerID ]->bTeam; + + // AI + if (team == 1) + team = 4; + // Clients + else if (team >= 6) + team -= 6; + else if (team == 0) + team = CLIENT_NUM-1; // this case should not be possible, including as a precaution - gMPPlayerStats[team].misses++; + gMPPlayerStats[team].misses++; + } server->RPC("recievehitSTRUCT",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } @@ -274,12 +381,23 @@ void sendhitWINDOW(RPCParameters *rpcParameters) { EV_S_WINDOWHIT* miss = (EV_S_WINDOWHIT*)rpcParameters->input; - int team = MercPtrs[ miss->ubAttackerID ]->bTeam; - if (team == 1) team = 5; - else if (team >= 6) team -= 6; - else if (team == 0) team = CLIENT_NUM-1; // this case should not be possible, including as a precaution - gMPPlayerStats[team].misses++; + SOLDIERTYPE* pAttacker = MercPtrs[ miss->ubAttackerID ]; + if (pAttacker != NULL) + { + int team = MercPtrs[ miss->ubAttackerID ]->bTeam; + + // AI + if (team == 1) + team = 4; + // Clients + else if (team >= 6) + team -= 6; + else if (team == 0) + team = CLIENT_NUM-1; // this case should not be possible, including as a precaution + + gMPPlayerStats[team].misses++; + } server->RPC("recievehitWINDOW",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } @@ -287,24 +405,34 @@ void sendMISS(RPCParameters *rpcParameters) { EV_S_MISS* miss = (EV_S_MISS*)rpcParameters->input; - int team = MercPtrs[ miss->ubAttackerID ]->bTeam; - if (team == 1) team = 5; - else if (team >= 6) team -= 6; - else if (team == 0) team = CLIENT_NUM-1; // this case should not be possible, including as a precaution + SOLDIERTYPE* pAttacker = MercPtrs[ miss->ubAttackerID ]; + if (pAttacker != NULL) + { + int team = MercPtrs[ miss->ubAttackerID ]->bTeam; + + // AI + if (team == 1) + team = 4; + // Clients + else if (team >= 6) + team -= 6; + else if (team == 0) + team = CLIENT_NUM-1; // this case should not be possible, including as a precaution - gMPPlayerStats[team].misses++; + gMPPlayerStats[team].misses++; + } server->RPC("recieveMISS",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } void updatenetworksoldier(RPCParameters *rpcParameters) { server->RPC("UpdateSoldierFromNetwork",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); -} +} void Snull_team(RPCParameters *rpcParameters) { server->RPC("null_team",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); -} +} void sendFIREW(RPCParameters *rpcParameters) { @@ -342,6 +470,25 @@ void sendTEAMCHANGE(RPCParameters *rpcParameters) server->RPC("recieveTEAMCHANGE",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, true, 0, UNASSIGNED_NETWORK_ID,0); } +void requestSETID(SystemAddress addr) +{ + server->RPC("requestSETID","", 0, HIGH_PRIORITY, RELIABLE, 0, addr, false, 0, UNASSIGNED_NETWORK_ID,0); +} + +void receiveSETID(RPCParameters *rpcParameters) +{ + setID = atoi((const char *)rpcParameters->input); + + // WANNE: FILE TRANSFER: Send the files to the client + fltServer.Send(&fileList,server,rpcParameters->sender,setID,MEDIUM_PRIORITY,0,false, &incrementalReadInterface, 5000); +} + +//void sendALLFILESRECEIVED(RPCParameters *rpcParameters) +//{ +// ScreenMsg( FONT_BCOLOR_ORANGE, MSG_MPSYSTEM, L"%S", rpcParameters->input); +//} + + // //void rINT(RPCParameters *rpcParameters)//who is first //{ @@ -361,7 +508,7 @@ void startCOMBAT(RPCParameters *rpcParameters) { if(!( gTacticalStatus.uiFlags & INCOMBAT )) - { + { gTacticalStatus.uiFlags |= INCOMBAT; @@ -381,7 +528,7 @@ void sendREAL(RPCParameters *rpcParameters) - //ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"netbTeam %d is ready for realtime",rData->bteam );//diabled unless testing as cheats + //ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, L"netbTeam %d is ready for realtime",rData->bteam );//diabled unless testing as cheats if(readyteamreg[rData->bteam]==0) { readyteamreg[rData->bteam]=1;//register vote, to prevent double voting ;p~ //hayden @@ -397,21 +544,18 @@ void sendREAL(RPCParameters *rpcParameters) if(gTacticalStatus.Team[ b ].bTeamActive)numactiveteams++; }//same - //ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPServerMessage[5], numreadyteams, numactiveteams ); + //ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPServerMessage[5], numreadyteams, numactiveteams ); //check # clients ready for realtime if (numreadyteams >= numactiveteams) { //if all send notification for realtime changeover - //ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"Switching to realtime..." ); + //ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, L"Switching to realtime..." ); numreadyteams=0; memset( &readyteamreg , 0 , sizeof (int) * 10); server->RPC("gotoRT",(const char*)rpcParameters->input, (*rpcParameters).numberOfBitsOfData, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); - - } } - } // 20081222 - OJW @@ -483,6 +627,39 @@ void rSortArray(int* arr, int len) } } +// WANNE: FILE TRANSFER: Send all the settings the client needs to know for the file transfer before file transfer starts +void requestFILE_TRANSFER_SETTINGS(RPCParameters *rpcParameters) +{ + SystemAddress sender = rpcParameters->sender;//get senders address +#ifndef USE_VFS + STRING512 executableDir; + GetExecutableDirectory(executableDir); + + STRING512 fileTransferDir; + strcpy(fileTransferDir, executableDir); + strcat(fileTransferDir, "\\"); + strcat(fileTransferDir, gsFILE_TRANSFER_DIRECTORY_SERVER); + + // Replace all "/" with "\" from the server path + char* correctFileTransferDir = ReplaceCharactersInString_Server(fileTransferDir, "/", "\\"); + + filetransfersettings_struct fts; + + fts.syncClientsDirectory = gsSYNC_CLIENTS_MP_DIR; + strcpy(fts.fileTransferDirectory, correctFileTransferDir); + strcpy(fts.serverName, SERVER_NAME); + fts.totalTransferBytes = fileListTotalBytes; +#else + filetransfersettings_struct fts; + + fts.syncClientsDirectory = gsSYNC_CLIENTS_MP_DIR; + strcpy(fts.fileTransferDirectory, s_ServerId.GetServerId(vfs::Path(gzFileTransferDirectory)).utf8().c_str()); + strcpy(fts.serverName, SERVER_NAME); + fts.totalTransferBytes = fileListTotalBytes; +#endif + server->RPC("recieveFILE_TRANSFER_SETTINGS",(const char*)&fts, (int)sizeof(filetransfersettings_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); +} + //************************* //UNASSIGNED_SYSTEM_ADDRESS //START INTERNAL SERVER @@ -496,6 +673,26 @@ void requestSETTINGS(RPCParameters *rpcParameters ) { client_info* clinf = (client_info*)rpcParameters->input; + // OJW - 20090507 + // Disconnect if version is wrong + if (strcmp(clinf->client_version,MPVERSION)!=0) + { + CHAR16 verErrMsg[255]; + swprintf(verErrMsg,255, MPClientMessage[66], clinf->client_version,MPVERSION); + + // send disconnect reason only to this client + server->RPC("recieveDISCONNECTREASON",(const char*)&verErrMsg, (int)sizeof(CHAR16)*255*8, HIGH_PRIORITY, RELIABLE, 0, rpcParameters->sender, false, 0, UNASSIGNED_NETWORK_ID,0); + + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"CONNECTION REJECTED - CLIENT HAS WRONG VERSION"); + // disconnect this client + server->CloseConnection(rpcParameters->sender, true); + return; + } + + // WANNE: FILE TRANSFER: A client connected -> start the file transfer! + if (gsSYNC_CLIENTS_MP_DIR) + requestSETID(rpcParameters->sender); + //server assigned client numbers - hayden. SystemAddress sender = rpcParameters->sender;//get senders address int bslot = f_rec_num(0,blank);//get empty record slot @@ -504,86 +701,94 @@ void requestSETTINGS(RPCParameters *rpcParameters ) client_d[bslot].cl_number=new_cl_num; //record clients number - settings_struct lan; + settings_struct lan; - //lan.client_num = clinf->client_num;//old client assigned number - lan.client_num = new_cl_num; //new server assigned number - strcpy(lan.client_name , clinf->client_name); + //lan.client_num = clinf->client_num;//old client assigned number + lan.client_num = new_cl_num; //new server assigned number + strcpy(lan.client_name , clinf->client_name); - // OJW - 20081204 - strcpy(lan.server_name , SERVER_NAME); - memcpy(lan.client_edges,client_edges,sizeof(int)*4); - memcpy(lan.client_teams,client_teams,sizeof(int)*4); + lan.RANDOM_SPAWN = RANDOM_SPAWN; + lan.RANDOM_MERCS = RANDOM_MERCS; - lan.RANDOM_SPAWN = RANDOM_SPAWN; - lan.RANDOM_MERCS = RANDOM_MERCS; + lan.max_clients = gsMAX_CLIENTS; + memcpy(lan.kitbag , kbag,sizeof (char)*100); + lan.damage_multiplier = gsDAMAGE_MULTIPLIER; - lan.max_clients = gsMAX_CLIENTS; - memcpy(lan.kitbag , kbag,sizeof (char)*100); - lan.damage_multiplier = gsDAMAGE_MULTIPLIER; - - lan.same_merc = gsSAME_MERC; - lan.gsMercArriveSectorX=gsMercArriveSectorX; - lan.gsMercArriveSectorY=gsMercArriveSectorY; + lan.same_merc = gsSAME_MERC; + lan.gsMercArriveSectorX=gsMercArriveSectorX; + lan.gsMercArriveSectorY=gsMercArriveSectorY; - lan.ENEMY_ENABLED=ENEMY_ENABLED; - lan.CREATURE_ENABLED=CREATURE_ENABLED; - lan.MILITIA_ENABLED=MILITIA_ENABLED; - lan.CIV_ENABLED=CIV_ENABLED; + lan.ENEMY_ENABLED=ENEMY_ENABLED; + lan.CREATURE_ENABLED=CREATURE_ENABLED; + lan.MILITIA_ENABLED=MILITIA_ENABLED; + lan.CIV_ENABLED=CIV_ENABLED; - lan.gsPLAYER_BSIDE=gsPLAYER_BSIDE; + lan.gsPLAYER_BSIDE=gsPLAYER_BSIDE; - lan.emorale=gsMORALE; - lan.gsREPORT_NAME=gsREPORT_NAME; - //something new - lan.secs_per_tick=gssecs_per_tick; - lan.soubBobbyRay=gGameOptions.ubBobbyRay; - lan.sofGunNut=gGameOptions.fGunNut; - lan.soubGameStyle=gGameOptions.ubGameStyle; - lan.soubDifficultyLevel=gGameOptions.ubDifficultyLevel; - lan.sofTurnTimeLimit=gGameOptions.fTurnTimeLimit; - lan.sofIronManMode=gGameOptions.fIronManMode; - lan.starting_balance=gsstarting_balance; + lan.emorale=gsMORALE; + lan.gsREPORT_NAME=gsREPORT_NAME; +//something new + lan.secs_per_tick=gssecs_per_tick; + lan.soubBobbyRay=gGameOptions.ubBobbyRay; + lan.sofGunNut=gGameOptions.fGunNut; + lan.soubGameStyle=gGameOptions.ubGameStyle; + lan.soubDifficultyLevel=gsDIFFICULT_LEVEL; + lan.sofTurnTimeLimit=gGameOptions.fTurnTimeLimit; + lan.sofIronManMode=gGameOptions.fIronManMode; + lan.starting_balance=gsstarting_balance; + + // OJW - 20090319 - Changing allow custom NIV to force NIV on all clients + // as with the new join screen setup, no way for the individual client to choose + // whether or not to use NIV. If this is wrong, its easily fixed... + // just change description text for the toggle option on the host screen to + // "Allow custom inventory" and delete this if statement. + if (sALLOW_CUSTOM_NIV==1 && IsNIVModeValid(true)) + gGameOptions.ubInventorySystem = INVENTORY_NEW; + else + gGameOptions.ubInventorySystem = INVENTORY_OLD; - // OJW - 20090319 - Changing allow custom NIV to force NIV on all clients - // as with the new join screen setup, no way for the individual client to choose - // whether or not to use NIV. If this is wrong, its easily fixed... - // just change description text for the toggle option on the host screen to - // "Allow custom inventory" and delete this if statement. - if (sALLOW_CUSTOM_NIV==1) - gGameOptions.ubInventorySystem = INVENTORY_NEW; - - lan.sofNewInv=gGameOptions.ubInventorySystem; + lan.sofNewInv=gGameOptions.ubInventorySystem; - lan.soDis_Bobby=gsDis_Bobby; - lan.soDis_Equip=gsDis_Equip; + lan.soDis_Bobby=gsDis_Bobby; + lan.soDis_Equip=gsDis_Equip; - lan.gsMAX_MERCS=gsMAX_MERCS; - - memcpy( lan.client_names , client_names, sizeof( char ) * 4 * 30 ); - lan.team=clinf->team; + lan.gsMAX_MERCS=gsMAX_MERCS; + + memcpy( lan.client_names , client_names, sizeof( char ) * 4 * 30 ); + lan.team=clinf->team; + // OJW - 20090530 - fix teams not initialised properly + client_teams[ lan.client_num - 1 ] = lan.team; - lan.TESTING=gsTESTING; + lan.TESTING=gsTESTING; - // OJW - 20081218 - if (RANDOM_SPAWN) - lan.cl_edge = client_edges[lan.client_num-1]; - else - lan.cl_edge=clinf->cl_edge; + // OJW - 20081218 + if (RANDOM_SPAWN) + lan.cl_edge = client_edges[lan.client_num-1]; + else + lan.cl_edge=clinf->cl_edge; - // OJW - 20081223 - if (RANDOM_MERCS) - { - memcpy(lan.random_mercs, random_merc_teams[client_mercteam[lan.client_num - 1]], sizeof(int) * 7); - } + // OJW - 20081223 + if (RANDOM_MERCS) + { + memcpy(lan.random_mercs, random_merc_teams[client_mercteam[lan.client_num - 1]], sizeof(int) * 7); + } - lan.TIME=TIME; - lan.WEAPON_READIED_BONUS=sWEAPON_READIED_BONUS; - lan.ALLOW_CUSTOM_NIV=sALLOW_CUSTOM_NIV; - lan.DISABLE_SPEC_MODE=sDISABLE_SPEC_MODE; + lan.TIME=TIME; + lan.WEAPON_READIED_BONUS=sWEAPON_READIED_BONUS; + lan.ALLOW_CUSTOM_NIV=sALLOW_CUSTOM_NIV; + lan.DISABLE_SPEC_MODE=sDISABLE_SPEC_MODE; + // OJW - 20081204 + strcpy(lan.server_name , SERVER_NAME); + memcpy(lan.client_edges,client_edges,sizeof(int)*4); + memcpy(lan.client_teams,client_teams,sizeof(int)*4); + + // OJW - 20090507 + // send server version to client + strcpy(lan.server_version,MPVERSION); + + server->RPC("recieveSETTINGS",(const char*)&lan, (int)sizeof(settings_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); - server->RPC("recieveSETTINGS",(const char*)&lan, (int)sizeof(settings_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0); } // end if(can_joingame) } @@ -602,7 +807,7 @@ void send_mapchange(void) } else { - ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPServerMessage[45]); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPServerMessage[45]); } } @@ -620,34 +825,137 @@ void CheckIncomingConnection(Packet* p) // we dont want to allow this as thier game will be out of state if (!can_joingame()) { - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"CONNECTION REJECTED - GAME HAS STARTED"); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, L"CONNECTION REJECTED - GAME HAS STARTED"); // disconnect this client, no need to notify them as they will know if they disconnected // before receiving a settings packet that they were not allowed to join server->CloseConnection(p->systemAddress, true); } } +void AddFilesToSendList() +{ +#ifndef USE_VFS + STRING512 executableDir; + GetExecutableDirectory(executableDir); + + HANDLE hFileIn; + HCONTAINER hStack = CreateStack( 100000, 600 ); + WIN32_FIND_DATA find, inFind; + + find.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY; + inFind.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY; + + STRING512 fileTransferDir; + strcpy(fileTransferDir, executableDir); + + // WANNE: Build the complete file transfer directory for the server + strcat(fileTransferDir, "\\"); + strcat(fileTransferDir, gsFILE_TRANSFER_DIRECTORY_SERVER); + + char* correctFileTransferDir = ReplaceCharactersInString_Server(fileTransferDir, "/", "\\"); + if (DirectoryExists(correctFileTransferDir)) + { + strcat(correctFileTransferDir, "\\*.*"); + + hFileIn = FindFirstFile( correctFileTransferDir, &inFind ); + + // Get all files (recursive) inside the folder "cSubDir" == FILE_TRANSFER_DIR_SERVER + INT32 iNumFiles = GetFilesInDirectory( hStack, correctFileTransferDir, hFileIn, &inFind ); + + CHAR filename[600]; + INT32 i; + for ( i=0 ; iGetProfileStack(); + vfs::CVirtualProfile *prof = PS->GetProfile("_MULTIPLAYER"); + if(prof != PS->TopProfile()) + { + // there is not supposed to be another profile? + // output error message + return; + } + CTransferRules transferRules; + transferRules.InitFromTxtFile("transfer_rules.txt"); + vfs::IBaseLocation* loc = prof->GetLocation(""); + THROWIFFALSE(loc != NULL, "MP profile was successfully created, but the root directory is not included"); + vfs::IBaseLocation::Iterator it = loc->begin(); + int i=0; + for(; !it.end(); it.next(), i++) + { + vfs::Path const& valid_path = it.value()->GetFullPath(); + if(transferRules.ApplyRule(valid_path()) == CTransferRules::ACCEPT) + { + // transfer only those files that are not on the ignore list + vfs::tReadableFile* rfile = vfs::tReadableFile::Cast(it.value()); + if(!rfile) + { + continue; + } + vfs::UInt32 fsize = rfile->GetFileSize(); + fileListTotalBytes += (long)fsize; + if( (fsize>0) && rfile->OpenRead()) + { + std::vector data(fsize,0); + vfs::UInt32 has_read=0; + rfile->Read(&data[0], fsize, has_read); + rfile->Close(); + fileList.AddFile(valid_path().utf8().c_str(),&data[0], fsize,fsize,FileListNodeContext(0,0), false); + } + } + } + if(i == 0) + { + // no files? + //char* outputTransferDir = ReplaceCharactersInString_Server(correctFileTransferDir, "\\*.*", ""); + //ScreenMsg( FONT_RED, MSG_MPSYSTEM, L"The Sync. MP Directory '%S' does not exist on the server. Cannot send any files to the clients!", outputTransferDir); + } +#endif +} void start_server (void) { if(!is_server) { - - - +#ifndef USE_VFS + CIniReader iniReader("..\\Ja2_mp.ini"); +#else + CIniReader iniReader("Ja2_mp.ini"); +#endif //retrieve settings from .ini - + f_rec_num(1,blank);//wipe clean //char maxclients[30]; char port[30]; char SERVER_PORT[30]; //char MAX_CLIENTS[30] ; - +#ifndef USE_VFS // GetPrivateProfileString( "Ja2_mp Settings","MAX_CLIENTS", "", maxclients, MAX_PATH, "..\\Ja2_mp.ini" ); GetPrivateProfileString( "Ja2_mp Settings","SERVER_PORT", "", port, MAX_PATH, "..\\Ja2_mp.ini" ); // strcpy( MAX_CLIENTS , maxclients ); strcpy( SERVER_PORT, port ); - +#else + // GetPrivateProfileString( "Ja2_mp Settings","MAX_CLIENTS", "", maxclients, MAX_PATH, "..\\Ja2_mp.ini" ); + strcpy(port, iniReader.ReadString("Ja2_mp Settings","SERVER_PORT", "")); + // strcpy( MAX_CLIENTS , maxclients ); + strcpy( SERVER_PORT, port ); +#endif char ints[30]; char maxclients[30]; @@ -659,6 +967,7 @@ void start_server (void) char bteam4_enabled[30]; char player_bside[30]; + char difficult_level[30]; char sBalance[30]; char time_div[30]; @@ -668,6 +977,7 @@ void start_server (void) char sRandomEdges[30]; // OJW - 20081204 +#ifndef USE_VFS GetPrivateProfileString( "Ja2_mp Settings","SERVER_NAME", "", SERVER_NAME, MAX_PATH, "..\\Ja2_mp.ini" ); GetPrivateProfileString( "Ja2_mp Settings","SAME_MERC", "", hire_same_merc, MAX_PATH, "..\\Ja2_mp.ini" ); @@ -683,22 +993,66 @@ void start_server (void) GetPrivateProfileString( "Ja2_mp Settings","CIV_ENABLED", "", bteam4_enabled, MAX_PATH, "..\\Ja2_mp.ini" ); GetPrivateProfileString( "Ja2_mp Settings","GAME_MODE", "", player_bside, MAX_PATH, "..\\Ja2_mp.ini" ); + GetPrivateProfileString( "Ja2_mp Settings","DIFFICULT_LEVEL", "", difficult_level, MAX_PATH, "..\\Ja2_mp.ini" ); + // OJW - 20090304 - override max number of ai's in co-op + char sOverrideMaxAI[30]; + OVERRIDE_MAX_AI = 0; + GetPrivateProfileString( "Ja2_mp Settings","OVERRIDE_MAX_AI", "", sOverrideMaxAI, MAX_PATH, "..\\Ja2_mp.ini" ); + + // WANNE: FILE TRANSFER + GetPrivateProfileString( "Ja2_mp Settings", "FILE_TRANSFER_DIRECTORY", "Data-MP", gsFILE_TRANSFER_DIRECTORY_SERVER, MAX_PATH, "..\\Ja2_mp.ini" ); + + char sendFiles[30]; + GetPrivateProfileString( "Ja2_mp Settings", "SYNC_CLIENTS_MP_DIR", "", sendFiles, MAX_PATH, "..\\Ja2_mp.ini" ); + gsSYNC_CLIENTS_MP_DIR = atoi(sendFiles); +#else + strncpy(SERVER_NAME, iniReader.ReadString("Ja2_mp Settings","SERVER_NAME", ""), 30); + + strncpy(hire_same_merc, iniReader.ReadString("Ja2_mp Settings","SAME_MERC", ""), 30); + strncpy(mor, iniReader.ReadString("Ja2_mp Settings","DISABLE_MORALE", ""), 30); + strncpy(maxclients, iniReader.ReadString("Ja2_mp Settings","MAX_CLIENTS", ""), 30); + strncpy(net_div, iniReader.ReadString("Ja2_mp Settings","DAMAGE_MULTIPLIER", ""), 30); + strncpy(sRandomMercs, iniReader.ReadString("Ja2_mp Settings","RANDOM_MERCS", ""), 30); + strncpy(sRandomEdges, iniReader.ReadString("Ja2_mp Settings","RANDOM_EDGES", ""), 30); + + strncpy(bteam1_enabled, iniReader.ReadString("Ja2_mp Settings","ENEMY_ENABLED", ""), 30); + //GetPrivateProfileString( "Ja2_mp Settings","CREATURE_ENABLED", "", bteam2_enabled, MAX_PATH, "..\\Ja2_mp.ini" ); + strncpy(bteam3_enabled, iniReader.ReadString("Ja2_mp Settings","MILITIA_ENABLED", ""), 30); + strncpy(bteam4_enabled, iniReader.ReadString("Ja2_mp Settings","CIV_ENABLED", ""), 30); + + strncpy(player_bside, iniReader.ReadString( "Ja2_mp Settings","GAME_MODE", ""), 30); + strncpy(difficult_level, iniReader.ReadString( "Ja2_mp Settings", "DIFFICULT_LEVEL", ""), 30); + + // OJW - 20090304 - override max number of ai's in co-op + char sOverrideMaxAI[30]; + OVERRIDE_MAX_AI = 0; + strncpy(sOverrideMaxAI, iniReader.ReadString("Ja2_mp Settings","OVERRIDE_MAX_AI", ""), 30); + + // WANNE: FILE TRANSFER + strncpy(gsFILE_TRANSFER_DIRECTORY_SERVER, iniReader.ReadString("Ja2_mp Settings", "FILE_TRANSFER_DIRECTORY", "Data-MP"), 100 ); + + char sendFiles[30]; + strncpy(sendFiles, iniReader.ReadString( "Ja2_mp Settings", "SYNC_CLIENTS_MP_DIR", ""), 30 ); + gsSYNC_CLIENTS_MP_DIR = atoi(sendFiles); + +#endif + gsSYNC_CLIENTS_MP_DIR = atoi(sendFiles); //something new gsMORALE=0; if(atoi(mor)==1)gsMORALE=1; - - + +#ifndef USE_VFS GetPrivateProfileString( "Ja2_mp Settings","KIT_BAG", "", kbag, MAX_PATH, "..\\Ja2_mp.ini" ); - + char rpn[30]; GetPrivateProfileString( "Ja2_mp Settings","REPORT_NAME", "", rpn, MAX_PATH, "..\\Ja2_mp.ini" ); gsREPORT_NAME=atoi(rpn); GetPrivateProfileString( "Ja2_mp Settings","STARTING_BALANCE", "", sBalance, MAX_PATH, "..\\Ja2_mp.ini" ); GetPrivateProfileString( "Ja2_mp Settings","TIMED_TURN_SECS_PER_TICK", "", time_div, MAX_PATH, "..\\Ja2_mp.ini" ); - + char dis_bob[30]; char dis_equip[30]; char max_merc[30]; @@ -709,44 +1063,69 @@ void start_server (void) GetPrivateProfileString( "Ja2_mp Settings","TESTING", "", test, MAX_PATH, "..\\Ja2_mp.ini" ); gsDis_Bobby=false; gsDis_Equip=false; +#else + strncpy(kbag, iniReader.ReadString("Ja2_mp Settings","KIT_BAG", ""), 100); - if(atoi(dis_bob)==1)gsDis_Bobby=true; - if(atoi(dis_equip)==1)gsDis_Equip=true; + char rpn[30]; + strncpy(rpn, iniReader.ReadString("Ja2_mp Settings","REPORT_NAME", ""), 30); + gsREPORT_NAME=atoi(rpn); - gsMAX_MERCS=atoi(max_merc); + strncpy(sBalance, iniReader.ReadString("Ja2_mp Settings","STARTING_BALANCE", ""), 30); + strncpy(time_div, iniReader.ReadString("Ja2_mp Settings","TIMED_TURN_SECS_PER_TICK", ""), 30); + + char dis_bob[30]; + char dis_equip[30]; + char max_merc[30]; + char test[30]; + strncpy(dis_bob, iniReader.ReadString("Ja2_mp Settings","DISABLE_BOBBY_RAYS", ""), 30); + strncpy(dis_equip, iniReader.ReadString("Ja2_mp Settings","DISABLE_AIM_AND_MERC_EQUIP", ""), 30); + strncpy(max_merc, iniReader.ReadString("Ja2_mp Settings","MAX_MERCS", ""), 30); + strncpy(test, iniReader.ReadString("Ja2_mp Settings","TESTING", ""), 30); + gsDis_Bobby=false; + gsDis_Equip=false; +#endif + + if(atoi(dis_bob)==1)gsDis_Bobby=true; + if(atoi(dis_equip)==1)gsDis_Equip=true; + + gsMAX_MERCS=atoi(max_merc); gsTESTING = atoi(test); gsPLAYER_BSIDE = atoi(player_bside); - ENEMY_ENABLED=0; - CREATURE_ENABLED=0; - MILITIA_ENABLED=0; - CIV_ENABLED=0; + gsDIFFICULT_LEVEL = atoi(difficult_level); + ENEMY_ENABLED=0; + CREATURE_ENABLED=0; + MILITIA_ENABLED=0; + CIV_ENABLED=0; - RANDOM_MERCS = atoi(sRandomMercs); - RANDOM_SPAWN = atoi(sRandomEdges); + RANDOM_MERCS = atoi(sRandomMercs); + RANDOM_SPAWN = atoi(sRandomEdges); - if (RANDOM_SPAWN) - { - // create random starting edges - int spawns[4] = { 0 , 1 , 2 , 3 }; - rSortArray(spawns,4); - memcpy(client_edges,spawns,sizeof(int)*4); - } + if (RANDOM_SPAWN) + { + // create random starting edges + int spawns[4] = { 0 , 1 , 2 , 3 }; + rSortArray(spawns,4); + memcpy(client_edges,spawns,sizeof(int)*4); + } - if (RANDOM_MERCS) - { + if (RANDOM_MERCS) + { // randomly sort team indexes to give client - // one of four random merc teams + // one of four random merc teams rSortArray(client_mercteam,4); - } + } if(gsPLAYER_BSIDE==2)//only enable ai during coop { ENEMY_ENABLED = 1; // always enable enemies in co-op MILITIA_ENABLED = atoi(bteam3_enabled); CIV_ENABLED = atoi(bteam4_enabled); - } + // OJW - 20090403 - override max ais + if (atoi(sOverrideMaxAI)==1) + OVERRIDE_MAX_AI = 1; + } gsSAME_MERC = atoi(hire_same_merc); gsDAMAGE_MULTIPLIER =(FLOAT)atof(net_div); @@ -755,7 +1134,7 @@ void start_server (void) gssecs_per_tick=atoi(time_div) ; gsstarting_balance=atoi(sBalance); - +#ifndef USE_VFS char time[30]; GetPrivateProfileString( "Ja2_mp Settings","TIME", "", time, MAX_PATH, "..\\Ja2_mp.ini" ); TIME=(FLOAT)atof(time); @@ -764,31 +1143,42 @@ void start_server (void) GetPrivateProfileString( "Ja2_mp Settings","WEAPON_READIED_BONUS", "", wpr, MAX_PATH, "..\\Ja2_mp.ini" ); sWEAPON_READIED_BONUS=atoi(wpr); - char acniv[30]; + char acniv[30]; GetPrivateProfileString( "Ja2_mp Settings","ALLOW_CUSTOM_NIV", "", acniv, MAX_PATH, "..\\Ja2_mp.ini" ); sALLOW_CUSTOM_NIV=atoi(acniv); - char dspec[30]; + char dspec[30]; GetPrivateProfileString( "Ja2_mp Settings","DISABLE_SPEC_MODE", "", dspec, MAX_PATH, "..\\Ja2_mp.ini" ); sDISABLE_SPEC_MODE=atoi(dspec); +#else + char time[30]; + strncpy(time, iniReader.ReadString("Ja2_mp Settings","TIME", ""), 30); + TIME=(FLOAT)atof(time); + char wpr[30]; + strncpy(wpr, iniReader.ReadString("Ja2_mp Settings","WEAPON_READIED_BONUS", ""), 30); + sWEAPON_READIED_BONUS=atoi(wpr); + + char acniv[30]; + strncpy(acniv, iniReader.ReadString("Ja2_mp Settings","ALLOW_CUSTOM_NIV", ""), 30); + sALLOW_CUSTOM_NIV=atoi(acniv); + + char dspec[30]; + strncpy(dspec, iniReader.ReadString("Ja2_mp Settings","DISABLE_SPEC_MODE", ""), 30); + sDISABLE_SPEC_MODE=atoi(dspec); +#endif //********************** + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, MPServerMessage[0] ); - - - ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPServerMessage[0] ); - server=RakNetworkFactory::GetRakPeerInterface(); bool b = server->Startup(gsMAX_CLIENTS, 30, &SocketDescriptor(atoi(SERVER_PORT),0), 1); - + server->SetMaximumIncomingConnections((gsMAX_CLIENTS)); - - - - //RPC's + //RPC's REGISTER_STATIC_RPC(server, sendPATH); + REGISTER_STATIC_RPC(server, sendDOWNLOADSTATUS); REGISTER_STATIC_RPC(server, sendSTANCE); REGISTER_STATIC_RPC(server, sendDIR); REGISTER_STATIC_RPC(server, sendFIRE); @@ -804,6 +1194,7 @@ void start_server (void) REGISTER_STATIC_RPC(server, sendGUI); REGISTER_STATIC_RPC(server, sendBULLET); REGISTER_STATIC_RPC(server, requestSETTINGS); + REGISTER_STATIC_RPC(server, requestFILE_TRANSFER_SETTINGS); REGISTER_STATIC_RPC(server, sendSTATE); REGISTER_STATIC_RPC(server, sendDEATH); REGISTER_STATIC_RPC(server, sendhitSTRUCT); @@ -811,55 +1202,64 @@ void start_server (void) REGISTER_STATIC_RPC(server, sendMISS); //REGISTER_STATIC_RPC(server, rOVH); - REGISTER_STATIC_RPC(server, updatenetworksoldier); - REGISTER_STATIC_RPC(server, Snull_team); - REGISTER_STATIC_RPC(server, sendFIREW); - REGISTER_STATIC_RPC(server, sendDOOR); - REGISTER_STATIC_RPC(server, endINTERRUPT); - REGISTER_STATIC_RPC(server, sendREAL); + REGISTER_STATIC_RPC(server, updatenetworksoldier); + REGISTER_STATIC_RPC(server, Snull_team); + REGISTER_STATIC_RPC(server, sendFIREW); + REGISTER_STATIC_RPC(server, sendDOOR); + REGISTER_STATIC_RPC(server, endINTERRUPT); + REGISTER_STATIC_RPC(server, sendREAL); REGISTER_STATIC_RPC(server, startCOMBAT); REGISTER_STATIC_RPC(server, sendWIPE); - REGISTER_STATIC_RPC(server, sendHEAL); - REGISTER_STATIC_RPC(server, sendEDGECHANGE); - REGISTER_STATIC_RPC(server, sendTEAMCHANGE); - REGISTER_STATIC_RPC(server, sendGAMEOVER); - REGISTER_STATIC_RPC(server, sendCHATMSG); - //REGISTER_STATIC_RPC(server, rINT); - // - + REGISTER_STATIC_RPC(server, sendHEAL); + REGISTER_STATIC_RPC(server, sendEDGECHANGE); + REGISTER_STATIC_RPC(server, sendTEAMCHANGE); + REGISTER_STATIC_RPC(server, sendGAMEOVER); + REGISTER_STATIC_RPC(server, sendCHATMSG); + REGISTER_STATIC_RPC(server, receiveSETID); + //REGISTER_STATIC_RPC(server, sendALLFILESRECEIVED); + //REGISTER_STATIC_RPC(server, rINT); + // if (b) { - ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPServerMessage[1]); - ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPServerMessage[2]); + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, MPServerMessage[1]); + //ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPServerMessage[2]); is_server = true; - + + + // WANNE: FILE TRANSFER + server->AttachPlugin(&fltServer); + server->SetSplitMessageProgressInterval(1); + fltServer.SetCallback(&serverFileListProgress); + + fileListTotalBytes=0; + if (gsSYNC_CLIENTS_MP_DIR == 1) + { + AddFilesToSendList(); + } + connect_client();//connect client to server } else - { - ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPServerMessage[4]); - + { + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPServerMessage[4]); } } else { - ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPServerMessage[3]); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPServerMessage[3]); } - - - - } + + // recieve and process server info packets void server_packet ( void ) -{ +{ Packet* p; - if (is_server) { @@ -876,55 +1276,61 @@ void server_packet ( void ) switch (SpacketIdentifier) { case ID_DISCONNECTION_NOTIFICATION://client disconnected purposefullly - // Connection lost normally - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"ID_DISCONNECTION_NOTIFICATION"); + // Connection lost normally + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_DISCONNECTION_NOTIFICATION"); HandleDisconnect(p->systemAddress);//clear record break; case ID_ALREADY_CONNECTED: // Connection lost normally - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"ID_ALREADY_CONNECTED"); + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_ALREADY_CONNECTED"); break; case ID_REMOTE_DISCONNECTION_NOTIFICATION: // Server telling the clients of another client disconnecting gracefully. You can manually broadcast this in a peer to peer enviroment if you want. - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"ID_REMOTE_DISCONNECTION_NOTIFICATION"); + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_REMOTE_DISCONNECTION_NOTIFICATION"); break; case ID_REMOTE_CONNECTION_LOST: // Server telling the clients of another client disconnecting forcefully. You can manually broadcast this in a peer to peer enviroment if you want. - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"ID_REMOTE_CONNECTION_LOST"); + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_REMOTE_CONNECTION_LOST"); break; case ID_REMOTE_NEW_INCOMING_CONNECTION: // Server telling the clients of another client connecting. You can manually broadcast this in a peer to peer enviroment if you want. - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"ID_REMOT/MING_CONNECTION"); + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_REMOT/MING_CONNECTION"); break; case ID_CONNECTION_ATTEMPT_FAILED: - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"ID_CONNECTION_ATTEMPT_FAILED"); + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_CONNECTION_ATTEMPT_FAILED"); break; case ID_NO_FREE_INCOMING_CONNECTIONS: - // Sorry, the server is full. I don't do anything here but + // Sorry, the server is full. I don't do anything here but // A real app should tell the user - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"ID_NO_FREE_INCOMING_CONNECTIONS"); + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_NO_FREE_INCOMING_CONNECTIONS"); break; case ID_CONNECTION_LOST: // Couldn't deliver a reliable packet - i.e. the other system was abnormally // terminated - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"ID_CONNECTION_LOST");//client dropped + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_CONNECTION_LOST");//client dropped HandleDisconnect(p->systemAddress);//clear record break; case ID_CONNECTION_REQUEST_ACCEPTED: // This tells the client they have connected - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"ID_CONNECTION_REQUEST_ACCEPTED"); + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_CONNECTION_REQUEST_ACCEPTED"); break; case ID_NEW_INCOMING_CONNECTION: //tells server client has connected - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"ID_NEW_INCOMING_CONNECTION"); + #ifdef JA2BETAVERSION + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_NEW_INCOMING_CONNECTION"); + #endif // make sure they can connect CheckIncomingConnection(p); //send_settings();//send off server set settings + break; case ID_MODIFIED_PACKET: // Cheater! - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"ID_MODIFIED_PACKET"); + #ifdef JA2BETAVERSION + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"ID_MODIFIED_PACKET"); + #endif break; default: - - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"** a packet has been recieved for which i dont know what to do... **"); + #ifdef JA2BETAVERSION + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, L"** a packet has been recieved for which i dont know what to do... **"); + #endif break; } @@ -956,14 +1362,16 @@ void server_disconnect (void) { if(is_server) { + server->DetachPlugin(&fltServer); server->Shutdown(300); is_server = false; + fileList.Clear(); // We're done with the network RakNetworkFactory::DestroyRakPeerInterface(server); - ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPServerMessage[6]); + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, MPServerMessage[6]); } else { - ScreenMsg( FONT_LTBLUE, MSG_CHAT, MPServerMessage[7]); + ScreenMsg( FONT_ORANGE, MSG_MPSYSTEM, MPServerMessage[7]); } -} \ No newline at end of file +} diff --git a/Multiplayer/test_space.cpp b/Multiplayer/test_space.cpp index 0eaf971f..04d9d2a8 100644 --- a/Multiplayer/test_space.cpp +++ b/Multiplayer/test_space.cpp @@ -108,7 +108,7 @@ bool ovh_ready; void test_func2 (void)//now bound to "0" { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"test_func2 - function testing ground:" ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"test_func2 - function testing ground:" ); SOLDIERTYPE * pSoldier=MercPtrs[ 0 ]; @@ -174,19 +174,19 @@ pSoldier->EVENT_InternalSetSoldierPosition( sCellX, sCellY ,FALSE, FALSE, FALSE //UIHandleLUIEndLock( NULL ); //if( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) ) -// ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"combat turn based" ); +// ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"combat turn based" ); //else -// ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"not" ); +// ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"not" ); //SOLDIERTYPE * pSoldier=MercPtrs[ 0 ]; // // if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags &( ANIM_FIREREADY | ANIM_FIRE ) ) ) // { -// ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"Ready" ); +// ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"Ready" ); // } // else -// ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"Not" ); +// ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"Not" ); ////fInterfacePanelDirty = DIRTYLEVEL2; ////guiPendingOverrideEvent = LU_ENDUILOCK; @@ -343,7 +343,7 @@ pSoldier->EVENT_InternalSetSoldierPosition( sCellX, sCellY ,FALSE, FALSE, FALSE sprintf(szDefault, "%s","hello"); - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"%S has connected.",szDefault );*/ + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"%S has connected.",szDefault );*/ //manual overide //manual_overide(); @@ -363,11 +363,11 @@ pSoldier->EVENT_InternalSetSoldierPosition( sCellX, sCellY ,FALSE, FALSE, FALSE // iBullet = CreateBullet( 0, 0, 0,11 ); // if (iBullet == -1) // { - // ScreenMsg( FONT_YELLOW, MSG_CHAT, L"Failed to create bullet"); + // ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"Failed to create bullet"); // } // pBullet = GetBulletPtr( iBullet ); - // //ScreenMsg( FONT_YELLOW, MSG_CHAT, L"Created Bullet"); + // //ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"Created Bullet"); // pBullet->fCheckForRoof=0; // pBullet->qIncrX=-917769; diff --git a/SCREENS.cpp b/SCREENS.cpp index 46b2745e..dbfae932 100644 --- a/SCREENS.cpp +++ b/SCREENS.cpp @@ -59,6 +59,7 @@ Screens GameScreens[MAX_SCREENS] = { MPHostScreenInit, MPHostScreenHandle, MPHostScreenShutdown }, { MPScoreScreenInit, MPScoreScreenHandle, MPScoreScreenShutdown }, // OJW - 20081222 { MPChatScreenInit, MPChatScreenHandle, MPChatScreenShutdown }, // OJW - 20090314 + { MPConnectScreenInit, MPConnectScreenHandle, MPConnectScreenShutdown }, // OJW - 20090422 #ifdef JA2BETAVERSION { AIViewerScreenInit, AIViewerScreenHandle, AIViewerScreenShutdown }, diff --git a/SaveLoadGame.cpp b/SaveLoadGame.cpp index 8fc212fe..81a78f80 100644 --- a/SaveLoadGame.cpp +++ b/SaveLoadGame.cpp @@ -121,6 +121,8 @@ #include "Mercs.h" #include "INIReader.h" +#include "VFS/vfs.h" + //rain #include "Rain.h" //end rain @@ -142,7 +144,6 @@ extern void ResetJA2ClockGlobalTimers( void ); extern void BeginLoadScreen( void ); extern void EndLoadScreen(); - extern CPostalService gPostalService; //Global variable used @@ -1920,6 +1921,7 @@ BOOLEAN Inventory::Save( HWFILE hFile, bool fSavingMap ) // The save directory now resides in the data directory (default or custom) BOOLEAN InitSaveDir() { +#ifndef USE_VFS // Look for a custom data dir first std::string dataDir = gCustomDataCat.GetRootDir(); if( dataDir.empty() || FileGetAttributes( (STR) dataDir.c_str() ) == 0xFFFFFFFF ) { @@ -1945,7 +1947,16 @@ BOOLEAN InitSaveDir() return FALSE; } } - +#else + if(is_networked) + { + sprintf( gSaveDir, "%s", utf8string::as_utf8(pMessageStrings[ MSG_MPSAVEDIRECTORY ] + 3).c_str() ); + } + else + { + sprintf( gSaveDir, "%s", utf8string::as_utf8(pMessageStrings[ MSG_SAVEDIRECTORY ] + 3).c_str() ); + } +#endif return TRUE; } @@ -6635,7 +6646,8 @@ void GetBestPossibleSectorXYZValues( INT16 *psSectorX, INT16 *psSectorY, INT8 *p //loop through all the mercs on the players team to find the one that is not moving for ( pSoldier = MercPtrs[ sSoldierCnt ]; sSoldierCnt <= bLastTeamID; sSoldierCnt++,pSoldier++) { - if( pSoldier->bActive ) + // test for !NULL (if initilization fails and MercPtrs contains 'NULL's) + if( pSoldier && pSoldier->bActive ) { if ( pSoldier->bAssignment != IN_TRANSIT && !pSoldier->flags.fBetweenSectors) { @@ -6659,7 +6671,7 @@ void GetBestPossibleSectorXYZValues( INT16 *psSectorX, INT16 *psSectorY, INT8 *p //loop through all the mercs and find one that is moving for ( pSoldier = MercPtrs[ sSoldierCnt ]; sSoldierCnt <= bLastTeamID; sSoldierCnt++,pSoldier++) { - if( pSoldier->bActive ) + if( pSoldier && pSoldier->bActive ) { //we found an alive, merc that is not moving *psSectorX = pSoldier->sSectorX; diff --git a/SaveLoadScreen.cpp b/SaveLoadScreen.cpp index f77f0448..82ed886f 100644 --- a/SaveLoadScreen.cpp +++ b/SaveLoadScreen.cpp @@ -204,8 +204,6 @@ extern BOOLEAN gfDisplaySaveGamesNowInvalidatedMsg; //Dealtar's Airport Externalization extern CPostalService gPostalService; - - // //Buttons // @@ -1032,7 +1030,7 @@ void GetSaveLoadScreenUserInput() bActiveTextField = (INT8)GetActiveFieldID(); if( bActiveTextField && bActiveTextField != -1 ) { - Get16BitStringFromField( (UINT8)bActiveTextField, gzGameDescTextField); + Get16BitStringFromField( (UINT8)bActiveTextField, gzGameDescTextField, 128 ); SetActiveField(0); DestroySaveLoadTextInputBoxes(); @@ -1081,7 +1079,7 @@ void SaveLoadGameNumber( INT8 bSaveGameID ) bActiveTextField = (INT8)GetActiveFieldID(); if( bActiveTextField && bActiveTextField != -1 ) { - Get16BitStringFromField( (UINT8)bActiveTextField, gzGameDescTextField ); + Get16BitStringFromField( (UINT8)bActiveTextField, gzGameDescTextField, 128 ); } //if there is save game in the slot, ask for confirmation before overwriting @@ -1504,7 +1502,8 @@ BOOLEAN LoadSavedGameHeader( INT8 bEntry, SAVED_GAME_HEADER *pSaveGameHeader ) //make sure the entry is valid if( bEntry < 0 || bEntry > NUM_SAVE_GAMES ) { - memset( &pSaveGameHeader, 0, sizeof( SAVED_GAME_HEADER ) ); + //memset( &pSaveGameHeader, 0, sizeof( SAVED_GAME_HEADER ) ); + memset( pSaveGameHeader, 0, sizeof( SAVED_GAME_HEADER ) ); return( FALSE ); } @@ -1761,7 +1760,7 @@ void SelectedSaveRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason ) bActiveTextField = (INT8)GetActiveFieldID(); if( bActiveTextField && bActiveTextField != -1 ) { - Get16BitStringFromField( (UINT8)bActiveTextField, gzGameDescTextField); + Get16BitStringFromField( (UINT8)bActiveTextField, gzGameDescTextField, 128 ); SetActiveField(0); DestroySaveLoadTextInputBoxes(); diff --git a/Standard Gaming Platform/DEBUG.H b/Standard Gaming Platform/DEBUG.H index 469abb5b..b8acff62 100644 --- a/Standard Gaming Platform/DEBUG.H +++ b/Standard Gaming Platform/DEBUG.H @@ -20,6 +20,8 @@ #include "TopicOps.h" #include "TopicIDs.h" +#include "VFS/vfs_types.h" + /* #ifdef __cplusplus extern "C" { @@ -55,7 +57,7 @@ inline void _Null() { } // // Uncomment the follow to turn on more aggressive assertions // -#define USE_AGGRESSIVE_ASSERTIONS +//#define USE_AGGRESSIVE_ASSERTIONS #ifdef FORCE_ASSERTS_ON @@ -215,6 +217,10 @@ extern void _DebugMessage(const char *pSourceFile, unsigned uiLineNum, const cha #define DebugAttackBusy(x) #endif +#include "VFS/vfs_debug.h" + +void _ExceptionMessage( CBasicException &ex ); + /* #ifdef __cplusplus } diff --git a/Standard Gaming Platform/DEBUG.cpp b/Standard Gaming Platform/DEBUG.cpp index ac6813ee..8bae2ac5 100644 --- a/Standard Gaming Platform/DEBUG.cpp +++ b/Standard Gaming Platform/DEBUG.cpp @@ -500,3 +500,22 @@ STR8 String(const STR8 String, ...) return gbTmpDebugString[usIndex]; } + +void _ExceptionMessage( CBasicException &ex ) +{ + g_ExceptionList.clear(); + CBasicException::CALLSTACK::iterator it = ex.m_CallStack.begin(); + for(; it!=ex.m_CallStack.end(); ++it) + { + SExceptionData exd; + exd.message = (*it).message; + exd.function = (*it).function; + exd.file = (*it).file; + exd.line = (*it).line; + g_ExceptionList.push_back(exd); + } + _FailMessage("",0,""); +} + + + diff --git a/Standard Gaming Platform/FileMan.cpp b/Standard Gaming Platform/FileMan.cpp index e1c6a500..fe466907 100644 --- a/Standard Gaming Platform/FileMan.cpp +++ b/Standard Gaming Platform/FileMan.cpp @@ -34,6 +34,7 @@ #include #include #include + #include "windows.h" #include "FileMan.h" @@ -45,6 +46,30 @@ #include "LibraryDataBase.h" #include "io.h" +#endif + +using namespace std; + +#include "VFS/vfs.h" + +#ifdef USE_VFS + +#include "VFS/vfs_file_raii.h" +#include + +struct SOperation +{ + enum EOperation + { + UNKNOWN, READ, WRITE, + }; + EOperation op; + SOperation() : op(UNKNOWN) {}; +}; + +typedef std::map tFILEMAP; +static tFILEMAP s_mapFiles; + #endif //************************************************************************** // @@ -230,6 +255,9 @@ void FileDebug( BOOLEAN f ) //************************************************************************** BOOLEAN FileExists( STR strFilename ) { +#ifdef USE_VFS + return GetVFS()->FileExists(vfs::Path(strFilename)); +#else // First check to see if it's in a library (most files should be there) if ( gFileDataBase.fInitialized && CheckIfFileExistInLibrary( strFilename ) ) return TRUE; @@ -246,6 +274,7 @@ BOOLEAN FileExists( STR strFilename ) return TRUE; return FALSE; +#endif } //************************************************************************** @@ -272,6 +301,9 @@ BOOLEAN FileExists( STR strFilename ) //************************************************************************** extern BOOLEAN FileExistsNoDB( STR strFilename ) { +#ifdef USE_VFS + return GetVFS()->FileExists(vfs::Path(strFilename)); +#else // First check if it's in the custom Data directory if ( gCustomDataCat.FindFile(strFilename) ) return TRUE; @@ -284,6 +316,7 @@ extern BOOLEAN FileExistsNoDB( STR strFilename ) return TRUE; return FALSE; +#endif } //************************************************************************** @@ -308,12 +341,16 @@ extern BOOLEAN FileExistsNoDB( STR strFilename ) //************************************************************************** BOOLEAN FileDelete( STR strFilename ) { +#ifdef USE_VFS + return GetVFS()->RemoveFileFromFS(vfs::Path(strFilename)); +#else // Snap: delete the file from the default Data catalogue (if it is there) // Since the path can be either relative or absolute, try both methods gDefaultDataCat.RemoveFile(strFilename, true); gDefaultDataCat.RemoveFile(strFilename, false); return( DeleteFile( (LPCSTR) strFilename ) ); +#endif } //************************************************************************** @@ -343,6 +380,41 @@ BOOLEAN FileDelete( STR strFilename ) //************************************************************************** HWFILE FileOpen( STR strFilename, UINT32 uiOptions, BOOLEAN fDeleteOnClose ) { +#ifdef USE_VFS + vfs::Path path(strFilename); + vfs::IBaseFile *pFile = NULL; + try + { + if(uiOptions & FILE_ACCESS_WRITE) + { + // 'vfs::CVirtualFile::SF_TOP' should be enough, but if for some strange reason + // file creation fails, we will stop at a writeable profile + // and won't unintentionally mess up a file from another profile + vfs::COpenWriteFile open_w( path, true, false, vfs::CVirtualFile::SF_STOP_ON_WRITEABLE_PROFILE); + pFile = &open_w.file(); + open_w.release(); + s_mapFiles[pFile].op = SOperation::WRITE; + return (HWFILE)pFile; + } + else if(uiOptions & FILE_ACCESS_READ) + { + vfs::COpenReadFile open_r(path, vfs::CVirtualFile::SF_TOP); + pFile = &open_r.file(); + open_r.release(); + s_mapFiles[pFile].op = SOperation::READ; + return (HWFILE)pFile; + } + } + // sometimes a file is supposed to opened that does not exist (not tested with FileExists()) + // this operation can fail with an exception that the calling code doesn't catch + // instead we catch it (any exception, not just CBasicException) here and return 0 + catch(CBasicException& ex) { LogException(ex); } + catch(...) + { + LogException( CBasicException("Caught undefined exception", _FUNCTION_FORMAT_, __LINE__, __FILE__) ); + } + return 0; +#else HWFILE hFile; HANDLE hRealFile; DWORD dwAccess; @@ -483,6 +555,7 @@ HWFILE FileOpen( STR strFilename, UINT32 uiOptions, BOOLEAN fDeleteOnClose ) return(0); return(hFile); +#endif } @@ -507,6 +580,14 @@ HWFILE FileOpen( STR strFilename, UINT32 uiOptions, BOOLEAN fDeleteOnClose ) void FileClose( HWFILE hFile ) { +#ifdef USE_VFS + vfs::IBaseFile *pFile = (vfs::IBaseFile*)hFile; + if(pFile) + { + pFile->Close(); + s_mapFiles.erase(pFile); + } +#else INT16 sLibraryID; UINT32 uiFileNum; @@ -535,6 +616,7 @@ void FileClose( HWFILE hFile ) if( gFileDataBase.fInitialized ) CloseLibraryFile( sLibraryID, uiFileNum ); } +#endif } //************************************************************************** @@ -573,6 +655,36 @@ void FileClose( HWFILE hFile ) BOOLEAN FileRead( HWFILE hFile, PTR pDest, UINT32 uiBytesToRead, UINT32 *puiBytesRead ) { +#ifdef USE_VFS +#ifdef JA2TESTVERSION + UINT32 uiStartTime = GetJA2Clock(); +#endif + bool bSuccess = false; + vfs::IBaseFile *pFile = (vfs::IBaseFile*)hFile; + if(pFile && (s_mapFiles[pFile].op == SOperation::READ)) + { + vfs::tReadableFile *pRF = vfs::tReadableFile::Cast(pFile); + if(pRF) + { + UINT32 uiBytesRead; + bSuccess = pRF->Read((vfs::Byte*)pDest, uiBytesToRead, uiBytesRead); + if(uiBytesToRead != uiBytesRead) + { + return FALSE; + } + if(puiBytesRead) + { + *puiBytesRead = uiBytesRead; + } + } + } +#ifdef JA2TESTVERSION + //Add the time that we spent in this function to the total. + uiTotalFileReadTime += GetJA2Clock() - uiStartTime; + uiTotalFileReadCalls++; +#endif + return bSuccess; +#else HANDLE hRealFile; DWORD dwNumBytesToRead, dwNumBytesRead; BOOLEAN fRet = FALSE; @@ -640,6 +752,7 @@ BOOLEAN FileRead( HWFILE hFile, PTR pDest, UINT32 uiBytesToRead, UINT32 *puiByte #endif return(fRet); +#endif } //************************************************************************** @@ -671,6 +784,29 @@ BOOLEAN FileRead( HWFILE hFile, PTR pDest, UINT32 uiBytesToRead, UINT32 *puiByte BOOLEAN FileWrite( HWFILE hFile, PTR pDest, UINT32 uiBytesToWrite, UINT32 *puiBytesWritten ) { +#ifdef USE_VFS + vfs::IBaseFile *pFile = (vfs::IBaseFile*)hFile; + if(pFile && (s_mapFiles[pFile].op == SOperation::WRITE)) + { + vfs::tWriteableFile *pWF = vfs::tWriteableFile::Cast(pFile); + if(pWF) + { + UINT32 uiBytesWritten; + bool bSuccess = pWF->Write((vfs::Byte*)pDest, uiBytesToWrite, uiBytesWritten); + + if (uiBytesToWrite != uiBytesWritten) + { + return FALSE; + } + if ( puiBytesWritten ) + { + *puiBytesWritten = uiBytesWritten; + } + return bSuccess; + } + } + return FALSE; +#else HANDLE hRealFile; DWORD dwNumBytesToWrite, dwNumBytesWritten; BOOLEAN fRet; @@ -705,6 +841,7 @@ BOOLEAN FileWrite( HWFILE hFile, PTR pDest, UINT32 uiBytesToWrite, UINT32 *puiBy } return(fRet); +#endif } //************************************************************************** @@ -730,6 +867,27 @@ BOOLEAN FileWrite( HWFILE hFile, PTR pDest, UINT32 uiBytesToWrite, UINT32 *puiBy BOOLEAN FileLoad( STR strFilename, PTR pDest, UINT32 uiBytesToRead, UINT32 *puiBytesRead ) { +#ifdef USE_VFS + vfs::tReadableFile *pFile = GetVFS()->GetRFile(vfs::Path(strFilename)); + if(pFile) + { + UINT32 uiNumBytesRead; + bool bSuccess = pFile->Read((vfs::Byte*)pDest,uiBytesToRead, uiNumBytesRead); + pFile->Close(); + + if (uiBytesToRead != uiNumBytesRead) + { + return FALSE; + } + if ( puiBytesRead ) + { + *puiBytesRead = uiNumBytesRead; + } + CHECKF( uiNumBytesRead == uiBytesToRead ); + return bSuccess; + } + return FALSE; +#else HWFILE hFile; UINT32 uiNumBytesRead; BOOLEAN fRet; @@ -752,6 +910,7 @@ BOOLEAN FileLoad( STR strFilename, PTR pDest, UINT32 uiBytesToRead, UINT32 *puiB fRet = FALSE; return(fRet); +#endif } //************************************************************************** @@ -784,6 +943,19 @@ BOOLEAN FileLoad( STR strFilename, PTR pDest, UINT32 uiBytesToRead, UINT32 *puiB BOOLEAN _cdecl FilePrintf( HWFILE hFile, STR8 strFormatted, ... ) { +#ifdef USE_VFS + CHAR8 strToSend[160]; /* itemdescription of item 0 will NOT fit if only 80 Chars per Line!, Sergeant_Kolja, 2007-06-10 */ + va_list argptr; + BOOLEAN fRetVal = FALSE; + + va_start(argptr, strFormatted); + _vsnprintf( strToSend, DIM(strToSend), strFormatted, argptr ); /* made StringLen Save, Sergeant_Kolja, 2007-06-10 */ + strToSend[ DIM(strToSend)-1 ] = 0; + va_end(argptr); + + fRetVal = FileWrite( hFile, strToSend, strlen(strToSend), NULL ); + return( fRetVal ); +#else CHAR8 strToSend[160]; /* itemdescription of item 0 will NOT fit if only 80 Chars per Line!, Sergeant_Kolja, 2007-06-10 */ va_list argptr; BOOLEAN fRetVal = FALSE; @@ -810,6 +982,7 @@ BOOLEAN _cdecl FilePrintf( HWFILE hFile, STR8 strFormatted, ... ) } return( fRetVal ); +#endif } //************************************************************************** @@ -839,6 +1012,53 @@ BOOLEAN _cdecl FilePrintf( HWFILE hFile, STR8 strFormatted, ... ) BOOLEAN FileSeek( HWFILE hFile, UINT32 uiDistance, UINT8 uiHow ) { +#ifdef USE_VFS + INT32 iDistance = (INT32)uiDistance; + + vfs::IBaseFile *pFile = (vfs::IBaseFile*)hFile; + if(pFile) + { + vfs::IBaseFile::ESeekDir eSD; + if ( uiHow == FILE_SEEK_FROM_START ) + { + eSD = vfs::IBaseFile::SD_BEGIN; + } + else if ( uiHow == FILE_SEEK_FROM_END ) + { + eSD = vfs::IBaseFile::SD_END; + if( iDistance > 0 ) + { + iDistance = -(iDistance); + } + } + else + { + eSD = vfs::IBaseFile::SD_CURRENT; + } + + if(s_mapFiles[pFile].op == SOperation::WRITE) + { + vfs::tWriteableFile *pWF = vfs::tWriteableFile::Cast(pFile); + if(pWF) + { + return pWF->SetWriteLocation(iDistance, eSD); + } + } + else if(s_mapFiles[pFile].op == SOperation::READ) + { + vfs::tReadableFile *pRF = vfs::tReadableFile::Cast(pFile); + if(pRF) + { + return pRF->SetReadLocation(iDistance, eSD); + } + } + else + { + THROWEXCEPTION(L"unknown operation"); + } + } + return FALSE; +#else HANDLE hRealFile; LONG lDistanceToMove; DWORD dwMoveMethod; @@ -881,6 +1101,7 @@ BOOLEAN FileSeek( HWFILE hFile, UINT32 uiDistance, UINT8 uiHow ) } return(TRUE); +#endif } //************************************************************************** @@ -908,6 +1129,30 @@ BOOLEAN FileSeek( HWFILE hFile, UINT32 uiDistance, UINT8 uiHow ) INT32 FileGetPos( HWFILE hFile ) { +#ifdef USE_VFS + vfs::IBaseFile *pFile = (vfs::IBaseFile*)hFile; + if(pFile) + { + if(pFile->IsWriteable()) + { + vfs::tWriteableFile *pWF = vfs::tWriteableFile::Cast(pFile); + if(pWF) + { + return pWF->GetWriteLocation(); + } + } + else if(pFile->IsReadable()) + { + vfs::tReadableFile *pRF = vfs::tReadableFile::Cast(pFile); + if(pRF) + { + return pRF->GetReadLocation(); + } + } + } + + return BAD_INDEX; +#else HANDLE hRealFile; UINT32 uiPositionInFile=0; @@ -944,6 +1189,7 @@ INT32 FileGetPos( HWFILE hFile ) } return(BAD_INDEX); +#endif } //************************************************************************** @@ -971,6 +1217,14 @@ INT32 FileGetPos( HWFILE hFile ) UINT32 FileGetSize( HWFILE hFile ) { +#ifdef USE_VFS + vfs::IBaseFile *pFile = (vfs::IBaseFile*)hFile; + if(pFile) + { + return pFile->GetFileSize(); + } + return 0; +#else HANDLE hRealHandle; UINT32 uiFileSize = 0xFFFFFFFF; @@ -999,6 +1253,7 @@ UINT32 FileGetSize( HWFILE hFile ) return(0); else return( uiFileSize ); +#endif } //************************************************************************** @@ -1250,6 +1505,10 @@ void BuildFileDirectory( void ) */ } + + + + //************************************************************************** // // GetFilesInDirectory @@ -1356,7 +1615,11 @@ BOOLEAN DirectoryExists( STRING512 pcDirectory ) BOOLEAN MakeFileManDirectory( STRING512 pcDirectory ) { +#ifndef USE_VFS return CreateDirectory( pcDirectory, NULL ); +#else + return FALSE; +#endif } @@ -1366,6 +1629,10 @@ BOOLEAN MakeFileManDirectory( STRING512 pcDirectory ) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// BOOLEAN RemoveFileManDirectory( STRING512 pcDirectory, BOOLEAN fRecursive ) { +#ifdef USE_VFS + // ignore 'recursive' flag, just delete every file in that subtree (but leave the directories) + return GetVFS()->RemoveDirectoryFromFS(pcDirectory); +#else WIN32_FIND_DATA sFindData; HANDLE SearchHandle; const CHAR8 *pFileSpec = "*.*"; @@ -1442,6 +1709,7 @@ BOOLEAN RemoveFileManDirectory( STRING512 pcDirectory, BOOLEAN fRecursive ) } return fRetval; +#endif } @@ -1451,6 +1719,10 @@ BOOLEAN RemoveFileManDirectory( STRING512 pcDirectory, BOOLEAN fRecursive ) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// BOOLEAN EraseDirectory( STRING512 pcDirectory) { +#ifdef USE_VFS + // ignore 'recursive' flag, just delete every file in that subtree (but leave the directories) + return GetVFS()->RemoveDirectoryFromFS(pcDirectory); +#else WIN32_FIND_DATA sFindData; HANDLE SearchHandle; const CHAR8 *pFileSpec = "*.*"; @@ -1502,6 +1774,7 @@ BOOLEAN EraseDirectory( STRING512 pcDirectory) } return( TRUE ); +#endif } @@ -1530,8 +1803,31 @@ BOOLEAN GetExecutableDirectory( STRING512 pcDirectory ) return( TRUE ); } +#ifdef USE_VFS +static vfs::CVirtualFileSystem::Iterator file_iter; +#endif BOOLEAN GetFileFirst( CHAR8 * pSpec, GETFILESTRUCT *pGFStruct ) { +#ifdef USE_VFS + CHECKF( pSpec != NULL ); + CHECKF( pGFStruct != NULL ); + + file_iter = GetVFS()->begin(pSpec); + if(!file_iter.end()) + { + //vfs::Path const& path = file_iter.value()->GetFullPath(); + vfs::Path const& path = file_iter.value()->GetFileName(); + std::string s = path().utf8(); + utf8string::size_t size = s.length(); + size = std::min(size,260-1); + sprintf( pGFStruct->zFileName, s.c_str()); + pGFStruct->zFileName[size] = 0; + + // don't care for the rest of variables in pGFStruct + return TRUE; + } + return FALSE; +#else INT32 x,iWhich=0; BOOLEAN fFound; @@ -1562,10 +1858,31 @@ BOOLEAN GetFileFirst( CHAR8 * pSpec, GETFILESTRUCT *pGFStruct ) W32toSGPFileFind( pGFStruct, &Win32FindInfo[iWhich] ); return(TRUE); +#endif } BOOLEAN GetFileNext( GETFILESTRUCT *pGFStruct ) { +#ifdef USE_VFS + if(!file_iter.end()) + { + file_iter.next(); + } + if(!file_iter.end()) + { + //vfs::Path const& path = file_iter.value()->GetFullPath(); + vfs::Path const& path = file_iter.value()->GetFileName(); + std::string s = path().utf8(); + utf8string::size_t size = s.length(); + size = std::min(size,260-1); + sprintf( pGFStruct->zFileName, s.c_str()); + pGFStruct->zFileName[size] = 0; + + // don't care for the rest of variables in pGFStruct + return TRUE; + } + return FALSE; +#else CHECKF( pGFStruct != NULL ); if ( FindNextFile(hFindInfoHandle[pGFStruct->iFindHandle], &Win32FindInfo[pGFStruct->iFindHandle]) ) @@ -1574,10 +1891,14 @@ BOOLEAN GetFileNext( GETFILESTRUCT *pGFStruct ) return(TRUE); } return(FALSE); +#endif } void GetFileClose( GETFILESTRUCT *pGFStruct ) { +#ifdef USE_VFS + file_iter = vfs::CVirtualFileSystem::Iterator(); +#else if ( pGFStruct == NULL ) return; @@ -1586,6 +1907,7 @@ void GetFileClose( GETFILESTRUCT *pGFStruct ) fFindInfoInUse[pGFStruct->iFindHandle] = FALSE; return; +#endif } void W32toSGPFileFind( GETFILESTRUCT *pGFStruct, WIN32_FIND_DATA *pW32Struct ) @@ -1802,13 +2124,45 @@ UINT32 FileGetAttributes( STR strFilename ) BOOLEAN FileClearAttributes( STR strFilename ) { +#ifndef USE_VFS return SetFileAttributes( (LPCSTR) strFilename, FILE_ATTRIBUTE_NORMAL ); +#else + return TRUE; +#endif } //returns true if at end of file, else false BOOLEAN FileCheckEndOfFile( HWFILE hFile ) { +#ifdef USE_VFS + UINT32 uiCurrentLocation, uiMaxLocation; + vfs::IBaseFile *pFile = (vfs::IBaseFile*)hFile; + if(pFile) + { + if(pFile->IsWriteable()) + { + vfs::tWriteableFile *pWF = vfs::tWriteableFile::Cast(pFile); + if(pWF) + { + uiCurrentLocation = pWF->GetWriteLocation(); + uiMaxLocation = pWF->GetFileSize(); + return uiCurrentLocation < uiMaxLocation; + } + } + else if(pFile->IsReadable()) + { + vfs::tReadableFile *pRF = vfs::tReadableFile::Cast(pFile); + if(pRF) + { + uiCurrentLocation = pRF->GetReadLocation(); + uiMaxLocation = pRF->GetFileSize(); + return uiCurrentLocation < uiMaxLocation; + } + } + } + return FALSE; +#else INT16 sLibraryID; UINT32 uiFileNum; HANDLE hRealFile; @@ -1873,6 +2227,7 @@ BOOLEAN FileCheckEndOfFile( HWFILE hFile ) //we are not and the end of a file return( 0 ); +#endif } @@ -1944,6 +2299,14 @@ INT32 CompareSGPFileTimes( SGP_FILETIME *pFirstFileTime, SGP_FILETIME *pSecondFi UINT32 FileSize(STR strFilename) { +#ifdef USE_VFS + vfs::IBaseFile *pFile = GetVFS()->GetFile(vfs::Path(strFilename)); + if(pFile) + { + return pFile->GetFileSize(); + } + return 0; +#else HWFILE hFile; UINT32 uiSize; @@ -1954,6 +2317,7 @@ UINT32 uiSize; FileClose(hFile); return(uiSize); +#endif } diff --git a/Standard Gaming Platform/FileMan.h b/Standard Gaming Platform/FileMan.h index a8f5a0c7..81b10348 100644 --- a/Standard Gaming Platform/FileMan.h +++ b/Standard Gaming Platform/FileMan.h @@ -25,6 +25,8 @@ #include "FileCat.h" +#include "Container.h" + //************************************************************************** // // Defines @@ -162,7 +164,7 @@ BOOLEAN FileCheckEndOfFile( HWFILE hFile ); BOOLEAN GetFileManFileTime( HWFILE hFile, SGP_FILETIME *pCreationTime, SGP_FILETIME *pLastAccessedTime, SGP_FILETIME *pLastWriteTime ); - +INT32 GetFilesInDirectory( HCONTAINER hStack, CHAR *pcDir, HANDLE hFile, WIN32_FIND_DATA *pFind ); diff --git a/Standard Gaming Platform/Font.cpp b/Standard Gaming Platform/Font.cpp index cc67092e..3accaeb1 100644 --- a/Standard Gaming Platform/Font.cpp +++ b/Standard Gaming Platform/Font.cpp @@ -27,6 +27,8 @@ #include "himage.h" #include "vobject.h" #include "vobject_blitters.h" + + #include #endif //******************************************************* // @@ -126,7 +128,12 @@ void SetFontForeground(UINT8 ubForeground) UINT32 uiRed, uiGreen, uiBlue; if((FontDefault < 0) || (FontDefault > MAX_FONTS)) - return; + { + //return; + std::wstringstream wss; + wss << L"invalid font index ( " << FontDefault << L" )"; + THROWEXCEPTION(wss.str().c_str()); + } FontForeground8=ubForeground; @@ -186,7 +193,12 @@ void SetFontBackground(UINT8 ubBackground) UINT32 uiRed, uiGreen, uiBlue; if((FontDefault < 0) || (FontDefault > MAX_FONTS)) - return; + { + //return; + std::wstringstream wss; + wss << L"invalid color background value ( " << ubBackground << L" )"; + THROWEXCEPTION(wss.str().c_str()); + } FontBackground8=ubBackground; @@ -582,6 +594,34 @@ INT16 StringPixLength(const STR16 string, INT32 UseFont) return((INT16)Cur); } +//***************************************************************************************** +// +// SubstringPixLength +// +// Return the length of the of a substring of a string +// +// Returns INT16 +// +// Created by: Owen Wigg +// Created on: 26/04/2009 +// +//***************************************************************************************** +INT16 SubstringPixLength(STR16 string, UINT32 iStart, UINT32 iEnd, INT32 UseFont) +{ + Assert (string != NULL); + Assert(iStart >= 0 && iStart <= iEnd); + Assert(iEnd < wcslen(string)); + + INT16 cnt = 0; + + for (unsigned int i=iStart; i <= iEnd; i++) + { + cnt += GetWidth(FontObjs[UseFont], GetIndex(string[i])); + } + + return cnt; +} + //***************************************************************************** // @@ -899,9 +939,12 @@ CHAR16 GetUnicodeChar(CHAR16 siChar) //***************************************************************************** BOOLEAN SetFont(INT32 iFontIndex) { - Assert(iFontIndex >= 0); - Assert(iFontIndex <= MAX_FONTS); - Assert(FontObjs[iFontIndex]!=NULL); + //Assert(iFontIndex >= 0); + //Assert(iFontIndex <= MAX_FONTS); + //Assert(FontObjs[iFontIndex]!=NULL); + THROWIFFALSE( iFontIndex >= 0 ,"negative font index"); + THROWIFFALSE( iFontIndex <= MAX_FONTS, "font index > MAX_FONTS" ); + THROWIFFALSE( FontObjs[iFontIndex]!=NULL, "font is not initialized" ); #ifdef WINFONTS SET_WINFONT(WinFontMap[iFontIndex]); #endif @@ -1407,6 +1450,14 @@ UINT8 *pDestBuf; desty+=GetHeight(FontObjs[FontDefault], transletter); } + // we get a ctd if we try try to write outside of the screen + if(desty + GetHeight(FontObjs[FontDefault], transletter) > SCREEN_HEIGHT) + { + // don't forget to unlock buffer + UnLockVideoSurface( FontDestBuffer ); + return 0; + } + // Blit directly if ( gbPixelDepth == 8 ) { diff --git a/Standard Gaming Platform/Font.h b/Standard Gaming Platform/Font.h index 65be389c..630f72d7 100644 --- a/Standard Gaming Platform/Font.h +++ b/Standard Gaming Platform/Font.h @@ -157,7 +157,7 @@ extern UINT32 GetWidth(HVOBJECT hSrcVObject, INT16 ssIndex); extern INT16 StringPixLengthArgFastHelp( INT32 usUseFont, INT32 usBoldFont, UINT32 uiCharCount, STR16 pFontString ); extern INT16 StringPixLengthArg(INT32 usUseFont, UINT32 uiCharCount, STR16 pFontString, ...); extern INT16 StringPixLength(const STR16 string,INT32 UseFont); - +extern INT16 SubstringPixLength(STR16 string, UINT32 iStart, UINT32 iEnd, INT32 UseFont); extern INT16 StringNPixLength(STR16 string, UINT32 uiMaxCount, INT32 UseFont); extern void SaveFontSettings(void); extern void RestoreFontSettings(void); diff --git a/Standard Gaming Platform/MemMan.cpp b/Standard Gaming Platform/MemMan.cpp index f53be2e6..befbc39f 100644 --- a/Standard Gaming Platform/MemMan.cpp +++ b/Standard Gaming Platform/MemMan.cpp @@ -36,6 +36,9 @@ #endif //#endif +#include "vfs.h" +#include "PropertyContainer.h" + #ifdef _DEBUG //#define DEBUG_MEM_LEAKS // turns on tracking of every MemAlloc and MemFree! #endif @@ -211,6 +214,7 @@ void ShutdownMemoryManager( void ) #ifndef EXTREME_MEMORY_DEBUGGING #ifdef JA2BETAVERSION { +#ifndef USE_VFS FILE *fp; fp = fopen( "MemLeakInfo.txt", "a" ); if( fp ) @@ -226,6 +230,18 @@ void ShutdownMemoryManager( void ) fprintf( fp, "\n\n" ); } fclose( fp ); +#else + CLog memLeak( L"MemLeakInfo.txt", true); + memLeak.Endl().Endl(); + memLeak << ">>>>> MEMORY LEAK DETECTED!!! <<<<<" << CLog::endl; + memLeak << " " << guiMemAlloced << " bytes memory total was allocated" << CLog::endl; + memLeak << "- " << guiMemFreed << " bytes memory total was freed" << CLog::endl; + memLeak << "_______________________________________________" << CLog::endl; + memLeak << guiMemTotal << " bytes memory total STILL allocated" << CLog::endl; + memLeak << MemDebugCounter << " memory blocks still allocated" << CLog::endl; + memLeak << "guiScreenExitedFrom = " << gzJA2ScreenNames[ gMsgBox.uiExitScreen ] << CLog::endl; + memLeak.Endl().Endl(); +#endif } #endif #endif diff --git a/Standard Gaming Platform/PngLoader.cpp b/Standard Gaming Platform/PngLoader.cpp new file mode 100644 index 00000000..2e3bd088 --- /dev/null +++ b/Standard Gaming Platform/PngLoader.cpp @@ -0,0 +1,1001 @@ +#include "PngLoader.h" +#include "FileMan.h" +#include "DEBUG.H" +#include "vobject.h" +#include "STIConvert.h" + +#include "VFS/vfs.h" +#include "VFS/vfs_file_raii.h" +#include "VFS/Location/vfs_7z_library.h" +#include "VFS/Interface/vfs_location_interface.h" +#include "VFS/File/vfs_memory_file.h" + +#include "XML_Parser.h" +namespace png +{ +#include +} + +const utf8string::str_t CONST_DOTPNG(L".png"); +const utf8string::str_t CONST_DOTXML(L".xml"); + +void Load24bppPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info); +void Load32bppPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info); +void LoadPalettedPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info); + + +void user_read_data(png::png_structp png_ptr, png::png_bytep data, png::png_size_t length) +{ + vfs::UInt32 read; + THROWIFFALSE( static_cast(png_ptr->io_ptr)->Read((vfs::Byte*)data,length, read),"error during png file reading"); +} + +/*******************************************************************************/ +/*******************************************************************************/ + +class IndexedSTIImage +{ +public: + IndexedSTIImage(); + ~IndexedSTIImage(); + + bool SetPalette(SGPPaletteEntry *pPal, int iSize); + bool SetPalette(png::png_colorp pPal, int iSize); + bool AddImage(UINT8 *data, UINT32 data_size, UINT32 image_width, UINT32 image_height, INT32 image_offset_x, INT32 image_offset_y, UINT8 *original_compressed=NULL, UINT32 original_compressed_size=0); + + bool AddCompressedImage(UINT8 *data, UINT32 data_size, UINT32 image_width, UINT32 image_height, INT32 image_offset_x, INT32 image_offset_y); + + bool ReadAppDataFromXMLFile(HIMAGE hImage, vfs::tReadableFile* pFile); + + bool WriteImage(vfs::tWriteableFile* pFile); + bool WriteToHIMAGE(HIMAGE pImage); +private: + STCIHeader _header; + STCIPaletteElement *_palette; + int _pal_size; + std::vector _images; + std::vector > _compressed_images; +}; + +IndexedSTIImage::IndexedSTIImage() +: _palette(NULL) +{ + memset(&_header, 0, sizeof(STCIHeader)); +} + +IndexedSTIImage::~IndexedSTIImage() +{ + if(_palette) + { + delete[] _palette; + } + for(unsigned int i=0; i < _compressed_images.size(); ++i) + { + //delete[] _compressed_images[i]; + } +} + +bool IndexedSTIImage::SetPalette(SGPPaletteEntry *pPal, int iSize) +{ + if(iSize < 0 ||iSize > 1024) + { + return false; + } + _pal_size = iSize; + _palette = new STCIPaletteElement[iSize]; + for(int i=0; i 1024) + { + return false; + } + _pal_size = iSize; + _palette = new STCIPaletteElement[iSize]; + for(int i=0; i 0, L"not enough space reserved for ETRLE compression"); + subimage.sOffsetX = image_offset_x; + subimage.sOffsetY = image_offset_y; + + //subimage.uiDataLength = compressed_size; + //_header.uiStoredSize += compressed_size; + subimage.uiDataLength = etrle_size; + _header.uiStoredSize += etrle_size; + _images.push_back(subimage); +// _compressed_images.push_back(compressed_start); + _header.uiOriginalSize += data_size; + _header.Indexed.usNumberOfSubImages += 1; + + return true; + +#if 0 + unsigned int uiBufferPos = 0; + + bool bZeroRun = false; + UINT8 uiRunLength = 0; + UINT8 *uiRunStartPosition = data; + if(*data == 0) + { + bZeroRun = true; + } + bool done = false; + UINT32 scanline = 0; + while(!done) + { + if(bZeroRun) + { + while((*data == 0) && (uiRunLength < 128) && (uiBufferPos < data_size) && (scanline < image_width)) + { + data++; + uiRunLength++; + uiBufferPos++; + scanline++; + } + uiRunStartPosition = compressed; + *compressed++ = uiRunLength | iCOMPRESS_TRANSPARENT; + compressed_size += 1; + } + else + { + uiRunStartPosition = compressed++; + while((*data != 0) && (uiRunLength < 128) && (uiBufferPos < data_size) && (scanline < image_width)) + { + *compressed++ = *data++; + uiRunLength++; + uiBufferPos++; + scanline++; + } + *uiRunStartPosition = uiRunLength; + compressed_size += uiRunLength+1; + } + // prepare next run + uiRunLength = 0; + bZeroRun = (*data != 0) ? false : true; + if(scanline == image_width) + { + scanline = 0; + // "close" scanline with a zero + *compressed++ = 0; + compressed_size += 1; + } + if(uiBufferPos >= data_size) + { + done = true; + } + } +#endif +} + + + + +inline void SetFlag(UINT8 &flags, char const* sFlag) +{ + /*0x01*/ if (strcmp(sFlag, "AUX_FULL_TILE") == 0) flags |= AUX_FULL_TILE; + /*0x02*/ else if(strcmp(sFlag, "AUX_ANIMATED_TILE") == 0) flags |= AUX_ANIMATED_TILE; + /*0x04*/ else if(strcmp(sFlag, "AUX_DYNAMIC_TILE") == 0) flags |= AUX_DYNAMIC_TILE; + /*0x08*/ else if(strcmp(sFlag, "AUX_INTERACTIVE_TILE") == 0) flags |= AUX_INTERACTIVE_TILE; + /*0x10*/ else if(strcmp(sFlag, "AUX_IGNORES_HEIGHT") == 0) flags |= AUX_IGNORES_HEIGHT; + /*0x20*/ else if(strcmp(sFlag, "AUX_USES_LAND_Z") == 0) flags |= AUX_USES_LAND_Z; + else + { + std::wstringstream wss; + wss << L"Unknown flag [" << utf8string::as_utf16(sFlag) << L"]"; + THROWEXCEPTION(wss.str().c_str()); + } +} + +class CAppDataParser : public IXMLParser +{ + enum DOM_OBJECT + { + DO_ELEMENT_NONE, + DO_ELEMENT_APPDATA, + DO_ELEMENT_AuxData, + DO_ELEMENT_flags, + DO_ELEMENT_properties, + }; +public: + CAppDataParser(XML_Parser &parser, IXMLParser* caller=NULL) + : IXMLParser("APPDATA", parser, caller), + current_state(DO_ELEMENT_NONE), + m_hImage(NULL), + m_iCurrentAux(-1) + { + } + virtual ~CAppDataParser() {}; + + virtual void OnStartElement(const XML_Char* name, const XML_Char** atts); + virtual void OnEndElement(const XML_Char* name); + virtual void OnTextElement(const XML_Char *str, int len); + + void SetImage(HIMAGE hImage) + { + m_hImage = hImage; + } +private: + DOM_OBJECT current_state; + HIMAGE m_hImage; + std::vector m_vAppData; + int m_iCurrentAux; +}; + +void CAppDataParser::OnStartElement(const XML_Char* name, const XML_Char** atts) +{ + if( current_state == DO_ELEMENT_NONE && strcmp(name, this->ElementName) == 0 ) + { + current_state = DO_ELEMENT_APPDATA; + } + else if(current_state == DO_ELEMENT_APPDATA && strcmp(name, "AuxObjectData") == 0) + { + int index = -1; + THROWIFFALSE(GetAttributeAsInt("index",atts,index), L"could not read attribute \"index\""); + m_iCurrentAux = index; + if(index >= (int)m_vAppData.size()) + { + m_vAppData.resize(index+1); + } + current_state = DO_ELEMENT_AuxData; + } + else if(current_state == DO_ELEMENT_AuxData && strcmp(name, "flags") == 0) + { + current_state = DO_ELEMENT_flags; + } + else if(current_state == DO_ELEMENT_flags) + { + SetFlag(m_vAppData[m_iCurrentAux].fFlags, name); + } + else if(current_state == DO_ELEMENT_AuxData && + ( strcmp(name, "ubCurrentFrame") == 0 || + strcmp(name, "ubNumberOfFrames") == 0 || + strcmp(name, "ubNumberOfTiles") == 0 || + strcmp(name, "ubWallOrientation") == 0 || + strcmp(name, "usTileLocIndex") == 0 ) + ) + { + current_state = DO_ELEMENT_properties; + } + sCharData = ""; +} +void CAppDataParser::OnEndElement(const XML_Char* name) +{ + char *p; + if( strcmp(name, "usTileLocIndex") == 0 && current_state == DO_ELEMENT_properties) + { + m_vAppData[m_iCurrentAux].usTileLocIndex = (UINT16)strtoul( sCharData.c_str(),&p,10); + current_state = DO_ELEMENT_AuxData; + } + else if( strcmp(name, "ubWallOrientation") == 0 && current_state == DO_ELEMENT_properties) + { + m_vAppData[m_iCurrentAux].ubWallOrientation = (UINT8)strtoul(sCharData.c_str(),&p,10); + current_state = DO_ELEMENT_AuxData; + } + else if( strcmp(name, "ubNumberOfTiles") == 0 && current_state == DO_ELEMENT_properties) + { + m_vAppData[m_iCurrentAux].ubNumberOfTiles = (UINT8)strtoul(sCharData.c_str(),&p,10); + current_state = DO_ELEMENT_AuxData; + } + else if( strcmp(name, "ubNumberOfFrames") == 0 && current_state == DO_ELEMENT_properties) + { + m_vAppData[m_iCurrentAux].ubNumberOfFrames = (UINT)strtoul(sCharData.c_str(),&p,10); + current_state = DO_ELEMENT_AuxData; + } + else if( strcmp(name, "ubCurrentFrame") == 0 && current_state == DO_ELEMENT_properties) + { + m_vAppData[m_iCurrentAux].ubCurrentFrame = (UINT8)strtoul(sCharData.c_str(),&p,10); + current_state = DO_ELEMENT_AuxData; + } + else if( strcmp(name, "flags") == 0 && current_state == DO_ELEMENT_flags) + { + current_state = DO_ELEMENT_AuxData; + } + else if( strcmp(name, "AuxObjectData") == 0 && current_state == DO_ELEMENT_AuxData) + { + current_state = DO_ELEMENT_APPDATA; + } + else if( strcmp(name, this->ElementName) == 0 && current_state == DO_ELEMENT_APPDATA) + { + int iAppDataSize = m_vAppData.size()*sizeof(AuxObjectData); + m_hImage->pAppData = (UINT8*) MemAlloc( iAppDataSize ); + for(unsigned int i = 0; i < m_vAppData.size(); ++i) + { + memcpy(&((AuxObjectData*)m_hImage->pAppData)[i],&m_vAppData[i],sizeof(AuxObjectData)); + } + m_hImage->uiAppDataSize = iAppDataSize; + current_state = DO_ELEMENT_NONE; + } +} +void CAppDataParser::OnTextElement(const XML_Char *str, int len) +{ + // handle only this special case; everything else does not matter for now + if(current_state == DO_ELEMENT_properties) + { + sCharData.append(str,len); + } + +} + + +bool IndexedSTIImage::ReadAppDataFromXMLFile(HIMAGE hImage, vfs::tReadableFile* pFile) +{ + if(!pFile) + { + return false; + } + vfs::COpenReadFile oFile(pFile); + UINT32 uiSize = oFile.file().GetFileSize(); + std::vector vBuffer(uiSize+1); + + UINT32 uiHasRead; + THROWIFFALSE( oFile.file().Read(&vBuffer[0],uiSize,uiHasRead) || (uiSize != uiHasRead), L"Could not read XML file"); + vBuffer[uiSize] = 0; + oFile.file().Close(); + + XML_Parser parser = XML_ParserCreate(NULL); + + CAppDataParser adp(parser); + adp.GrabParser(); + adp.SetImage(hImage); + + try + { + if(!XML_Parse(parser, &vBuffer[0], uiSize, TRUE)) + { + std::wstringstream wss; + wss << L"XML Parser Error in Groups.xml: " + << utf8string(XML_ErrorString(XML_GetErrorCode(parser))).c_wcs().c_str() + << L" at line " + << XML_GetCurrentLineNumber(parser); + THROWEXCEPTION(wss.str().c_str()); + } + } + catch(CBasicException &ex) + { + RETHROWEXCEPTION(L"Could not load xml file", &ex); + } + return true; +} + + +bool IndexedSTIImage::WriteToHIMAGE(HIMAGE pImage) +{ + if(!pImage) + { + return false; + } + pImage->usNumberOfObjects = _compressed_images.size(); + pImage->usWidth = 640; + pImage->usHeight = 480; + pImage->ubBitDepth = 8; +// pImage->fFlags = STCI_ETRLE_COMPRESSED | STCI_INDEXED | STCI_TRANSPARENT; + pImage->fFlags |= IMAGE_TRLECOMPRESSED; + pImage->fFlags |= IMAGE_BITMAPDATA; + + //strncat(pImage->ImageFile,"test",4); + pImage->iFileLoader = -1; + pImage->pui16BPPPalette = NULL; + + + pImage->pPalette = new SGPPaletteEntry[256]; + for(int j=0; j<256; ++j) + { + pImage->pPalette[j].peRed = _palette[j].ubRed; + pImage->pPalette[j].peGreen = _palette[j].ubGreen; + pImage->pPalette[j].peBlue = _palette[j].ubBlue; + pImage->pPalette[j].peFlags = 0; + } + + pImage->pETRLEObject = new ETRLEObject[pImage->usNumberOfObjects]; + UINT32 offset=0; // == size + for(unsigned int i=0; i<_images.size(); ++i) + { + pImage->pETRLEObject[i].sOffsetX = _images[i].sOffsetX; + pImage->pETRLEObject[i].sOffsetY = _images[i].sOffsetY; + pImage->pETRLEObject[i].usHeight = _images[i].usHeight; + pImage->pETRLEObject[i].usWidth = _images[i].usWidth; + pImage->pETRLEObject[i].uiDataOffset = offset; + pImage->pETRLEObject[i].uiDataLength = _images[i].uiDataLength; + offset += _images[i].uiDataLength; + } + + pImage->uiSizePixData = offset; + pImage->pPixData8 = new UINT8[offset]; + // write sub-images + for(unsigned int i=0; i<_compressed_images.size(); ++i) + { + memcpy( + &pImage->pPixData8[pImage->pETRLEObject[i].uiDataOffset], + &_compressed_images[i][0], + pImage->pETRLEObject[i].uiDataLength); + } + + //pImage->uiAppDataSize = pImage->usNumberOfObjects * sizeof(AuxObjectData); + //pImage->pAppData = (UINT8*)(new AuxObjectData[pImage->usNumberOfObjects]); + //memset(pImage->pAppData,0,pImage->uiAppDataSize); + + //// write application data + //int frames = _compressed_images.size()/8; // 8 = number of directions + //for(unsigned int i=0; iusNumberOfObjects; ++i) + //{ + // if(i % frames == 0) + // { + // AuxObjectData &aod = *((AuxObjectData*)(&pImage->pAppData[i*sizeof(AuxObjectData)])); + // aod.ubNumberOfFrames = frames; + // aod.fFlags = AUX_ANIMATED_TILE; + // } + //} + return true; +} + +/*******************************************************************************/ +/*******************************************************************************/ + +class LoadPngFile +{ +public: + LoadPngFile(vfs::Path const& sFile) + : _file(NULL), _struct(NULL), _info(NULL), _row_ptr(NULL) + { + // Open and read in the file + vfs::COpenReadFile oFile(sFile); + _file = &oFile.file(); + oFile.release(); + } + LoadPngFile(vfs::tReadableFile* pFile) + : _file(pFile), _struct(NULL), _info(NULL), _row_ptr(NULL) + { + THROWIFFALSE(_file, L"file pointer is NULL"); + } + bool Load() + { + png::png_error_ptr user_error_ptr = NULL; + png::png_error_ptr user_error_fn = NULL; + png::png_error_ptr user_warning_fn = NULL; + + _struct = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png::png_voidp)user_error_ptr, user_error_fn, user_warning_fn); + if (!_struct) + { + return false; + } + + _info = png_create_info_struct(_struct); + if (!_info) + { + png_destroy_read_struct(&_struct, (png::png_infopp)NULL, (png::png_infopp)NULL); + return false; + } + + png::png_infop end_info = png_create_info_struct(_struct); + if (!end_info) + { + png_destroy_read_struct(&_struct, &_info, (png::png_infopp)NULL); + return false; + } + + // push file handle + png_set_read_fn(_struct, _file, user_read_data); + + int png_transforms = PNG_TRANSFORM_IDENTITY; + png_read_png(_struct, _info, png_transforms, NULL); + +// png_read_end(_struct,_info); + + _row_ptr = png_get_rows(_struct, _info); + + return _row_ptr != NULL; + } + ~LoadPngFile() + { + if(_file) + { + _file->Close(); + } + if(_struct && _info) + { + png_destroy_info_struct(_struct, &_info); + _info = NULL; + } + if(_struct) + { + png_destroy_read_struct(&_struct, (png::png_infopp)_info, (png::png_infopp)NULL); + } + //if(_row_ptr) + //{ + // int i=0; + //} + } + + png::png_bytepp Rows() + { + return _row_ptr; + } + png::png_infop Info() + { + return _info; + } +private: + vfs::tReadableFile* _file; + png::png_structp _struct; + png::png_infop _info; + png::png_bytepp _row_ptr; + png::png_bytep _data; +}; + +bool LoadPNGFileToImage(HIMAGE hImage, UINT16 fContents) +{ + LoadPngFile fpng(hImage->ImageFile); + if(!fpng.Load()) + { + return false; + } + + if(fpng.Info()->channels == 4 && fpng.Info()->bit_depth == 8) + { + Load32bppPNGImage(hImage, fpng.Rows(), fpng.Info()); + } + else if(fpng.Info()->channels == 3 && fpng.Info()->bit_depth == 8) + { + Load24bppPNGImage(hImage, fpng.Rows(), fpng.Info()); + } + else if(fpng.Info()->channels == 1 && fpng.Info()->bit_depth == 8) + { + LoadPalettedPNGImage(hImage, fpng.Rows(), fpng.Info()); + } + else + { + std::wstringstream wss; + wss << L"Unknown image datatype : channels = " << (int)(fpng.Info()->channels) + << L", bit depth = " << (int)(fpng.Info()->bit_depth); + THROWEXCEPTION(wss.str().c_str()); + } + + // data was copied to hImage, need to clean up here + // ... + return true; +} + + +bool LoadJPCFileToImage(HIMAGE hImage, UINT16 fContents) +{ + if(!GetVFS()->FileExists(hImage->ImageFile)) + { + return false; + } + vfs::COpenReadFile oFile(hImage->ImageFile); + + vfs::CMemoryFile oBuffer(""); + THROWIFFALSE( oBuffer.CopyToBuffer(oFile.file()), L"Could not copy file to buffer"); + + //UINT32 uiSize = oFile.file().GetFileSize(); + //std::vector vBuffer(uiSize); + + //UINT uiHasRead, uiHasWritten; + //if( !oFile.file().Read(&vBuffer[0],uiSize,&uiHasRead) || uiHasRead != uiSize) + //{ + // THROWEXCEPTION(L"could not load file content to buffer"); + //} + + //oBuffer.OpenWrite(); + //if( !oBuffer.Write(&vBuffer[0],uiSize,&uiHasWritten) || uiSize != uiHasWritten) + //{ + // THROWEXCEPTION(L"cound not write buffer content into memory file"); + //} + oBuffer.Close(); + + //vfs::CUncompressed7zLibrary oLib(&oFile.file(),""); + ObjBlockAllocator allocator(128); + vfs::CUncompressed7zLibrary oLib(vfs::tReadableFile::Cast(&oBuffer),"",false, &allocator); + + if(!oLib.Init()) + { + return false; + } + + IndexedSTIImage image; + + std::vector vFiles; + int count_files = 0; + vfs::CUncompressed7zLibrary::Iterator it = oLib.begin(); + for(; !it.end(); it.next()) + { + count_files++; + } + vFiles.resize(count_files); + it = oLib.begin(); + for(; !it.end(); it.next()) + { + // check extension + utf8string::str_t const& fname = it.value()->GetFileName()().c_wcs(); + utf8string::size_t dot = fname.find_last_of(vfs::Const::DOT()); + if(dot != utf8string::str_t::npos) + { + if( StrCmp::Equal(fname.substr(dot,fname.length()-dot), CONST_DOTPNG) ) + { + std::wstringstream bss; + bss.str( fname.substr(0,dot) ); + int index; + bss >> index; + if(index >= 0 && index < count_files) + { + vFiles[index] = it.value(); + } + else + { + THROWEXCEPTION(L"invalid index"); + } + } + else if (StrCmp::Equal(fname.substr(dot,fname.length()-dot), CONST_DOTXML) ) + { + image.ReadAppDataFromXMLFile(hImage, vfs::tReadableFile::Cast(it.value())); + } + } + } + + bool bHasPalette = false; + std::vector::iterator fit = vFiles.begin(); + if(vFiles.size() == 1) + { + try + { + LoadPngFile lpng( vfs::tReadableFile::Cast(vFiles[0]) ); + + bool bLoadS = lpng.Load(); + + if(bLoadS) + { + if(lpng.Info()->channels == 4 && lpng.Info()->bit_depth == 8) + { + Load32bppPNGImage(hImage, lpng.Rows(), lpng.Info()); + } + else if(lpng.Info()->channels == 3 && lpng.Info()->bit_depth == 8) + { + Load24bppPNGImage(hImage, lpng.Rows(), lpng.Info()); + } + else if(lpng.Info()->channels == 1 && lpng.Info()->bit_depth == 8) + { + LoadPalettedPNGImage(hImage, lpng.Rows(), lpng.Info()); + } + else + { + std::wstringstream wss; + wss << L"Unknown image datatype : channels = " << (int)(lpng.Info()->channels) + << L", bit depth = " << (int)(lpng.Info()->bit_depth); + THROWEXCEPTION(wss.str().c_str()); + } + return true; + } + } + catch(CBasicException &ex) + { + std::wstringstream wss; + wss << L"Loading PNG image from file '" + << oFile.file().GetFullPath()().c_wcs() + << L"' failed"; + RETHROWEXCEPTION(wss.str().c_str(),&ex); + } + return false; + } + else + { + for(int findex = 0; fit != vFiles.end(); ++fit, ++findex) + { + if(*fit == NULL) + { + // probably not a PNG file + continue; + } + try + { + vfs::CMemoryFile oTempFile(""); + oTempFile.CopyToBuffer( *vfs::tReadableFile::Cast(*fit) ); + +// LoadPngFile lpng( vfs::tReadableFile::Cast(*fit) ); + LoadPngFile lpng( vfs::tReadableFile::Cast(&oTempFile) ); + + bool bLoadS = lpng.Load(); + if(bLoadS) + { + if(lpng.Info()->channels == 1 && lpng.Info()->bit_depth == 8) + { + if(!bHasPalette) + { + THROWIFFALSE(lpng.Info()->num_palette == 256, L"size of palette is not 256"); + image.SetPalette(lpng.Info()->palette, lpng.Info()->num_palette); + bHasPalette = true; + } + UINT32 SIZE = lpng.Info()->height * lpng.Info()->width; + std::vector data(SIZE,0); + for(unsigned int i = 0; i < lpng.Info()->height; ++i) + { + memcpy(&data[i*lpng.Info()->width],lpng.Rows()[i],lpng.Info()->width); + } + image.AddImage(&data[0], SIZE, lpng.Info()->width, lpng.Info()->height, lpng.Info()->x_offset, lpng.Info()->y_offset); + } + else + { + std::wstringstream wss; + wss << L"PNG file '" << (*fit)->GetFileName()() << L" @ " << oFile.file().GetFullPath()() << L"' is not a paletted image"; + THROWEXCEPTION(wss.str().c_str()); + } + } + } + catch(CBasicException& ex) + { + std::wstringstream wss; + wss << L"Loading PNG image [" << findex << L"] from file '" << oFile.file().GetFullPath()().c_wcs() << L"' failed"; + RETHROWEXCEPTION(wss.str().c_str(),&ex); + } + } + } + return image.WriteToHIMAGE(hImage); +} + + +void Load32bppPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info) +{ + hImage->usHeight = (UINT16)info->height; + hImage->usWidth = (UINT16)info->width; + hImage->ubBitDepth = 32; + +#if 1 + UINT32 SIZE = info->height * info->width; + hImage->p32BPPData = new UINT32[SIZE]; + memset(hImage->p32BPPData, 0, SIZE*sizeof(UINT32)); + UINT32* dest_row = NULL; + UINT32 rgbcolor = 0; + for(unsigned int i=0; iheight; ++i) + { + dest_row = &(hImage->p32BPPData[i*info->width]); + png::png_bytep row_i = rows[i]; + memcpy(dest_row,row_i,info->width * sizeof(UINT32)); + } +#else + UINT32 SIZE = info->height * info->width; + hImage->p16BPPData = new UINT16[SIZE]; + memset(hImage->p16BPPData, 0, SIZE*sizeof(UINT16)); + UINT16* dest_row = NULL; + UINT32 rgbcolor = 0; + for(unsigned int i=0; iheight; ++i) + { + dest_row = &(hImage->p16BPPData[i*info->width]); + png_bytep row_i = rows[i]; + for(unsigned int sx = 0, dx = 0; sx < 4*info->width; sx+=4, dx+=1) + { + if(row_i[sx+3] == 255) + { + rgbcolor = FROMRGB(row_i[sx], row_i[sx+1], row_i[sx+2]); + if(rgbcolor == 0) + { + // since we already use rgb(0,0,0) as a fully transparent color, + // the color black will be mapped to rgb(0,1,0), because green has the most bits + //rgbcolor = 1 << 8; + rgbcolor = FROMRGB(0,1,0); + } + dest_row[dx] = Get16BPPColor(rgbcolor); + } + else + { + dest_row[dx] = Get16BPPColor(0); + } + } + } +#endif +} + + +void Load24bppPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info) +{ + hImage->usHeight = (UINT16)info->height; + hImage->usWidth = (UINT16)info->width; + hImage->ubBitDepth = 16; + + UINT32 SIZE = info->height * info->width; + hImage->p16BPPData = new UINT16[SIZE]; + memset(hImage->p16BPPData, 0, SIZE*sizeof(UINT16)); + + UINT16* dest_row = NULL; + UINT32 rgbcolor = 0; + for(unsigned int i=0; iheight; ++i) + { + dest_row = &(hImage->p16BPPData[i*info->width]); + png::png_bytep row_i = rows[i]; + for(unsigned int sx = 0, dx = 0; sx < 3*info->width; sx+=3, dx+=1) + { + dest_row[dx] = Get16BPPColor( FROMRGB(row_i[sx], row_i[sx+1], row_i[sx+2]) ); + } + } +} + +void LoadPalettedPNGImage(HIMAGE hImage, png::png_bytepp rows, png::png_infop info) +{ + UINT32 SIZE = info->height * info->width; + UINT8 *data = new UINT8[SIZE]; + memset(data,0,SIZE); + for(unsigned int i = 0; i < info->height; ++i) + { + memcpy(&data[i*info->width],rows[i],info->width); + } + + hImage->usNumberOfObjects = 1; + hImage->pETRLEObject = new ETRLEObject[hImage->usNumberOfObjects]; + memset(hImage->pETRLEObject,0, hImage->usNumberOfObjects * sizeof(ETRLEObject)); + + ETRLEObject &subimage = hImage->pETRLEObject[0]; + subimage.sOffsetX = (INT16) info->x_offset;; + subimage.sOffsetY = (INT16) info->y_offset; + subimage.usWidth = (UINT16) info->width; + subimage.usHeight = (UINT16) info->height; + + // why allocate so much? because in in the worst case we need one TAG and one DATAVALUE per pixel + UINT32 RESIZE = 3*SIZE; + hImage->p8BPPData = new UINT8[RESIZE]; + UINT8 *compressed = hImage->p8BPPData; + memset(hImage->p8BPPData,0,RESIZE); + UINT32 compressed_size = 0; + + STCISubImage subim; + memset(&subim,0,sizeof(STCISubImage)); + + subim.uiDataLength = subimage.uiDataLength; + subim.uiDataOffset = subimage.uiDataOffset; + subim.usHeight = subimage.usHeight; + subim.usWidth = subimage.usWidth; + + UINT32 etrle_size = ETRLECompressSubImage( hImage->p8BPPData, RESIZE, data, (UINT16)info->width, (UINT16)info->height, &subim); + THROWIFFALSE( etrle_size > 0, L"ETRLE compression of PNG image failed" ); + subim.sOffsetX = subimage.sOffsetX; + subim.sOffsetY = subimage.sOffsetY; +#if 0 + unsigned int uiBufferPos = 0; + + bool bZeroRun = false; + UINT8 uiRunLength = 0; + UINT8 *uiRunStartPosition = data; + if(*data == 0) + { + bZeroRun = true; + } + bool done = false; + UINT32 scanline = 0; + while(!done) + { + if(bZeroRun) + { + while((*data == 0) && (uiRunLength < 128) && (uiBufferPos < SIZE) && (scanline < info->width)) + { + data++; + uiRunLength++; + uiBufferPos++; + scanline++; + } + uiRunStartPosition = compressed; + *compressed++ = uiRunLength | COMPRESS_TRANSPARENT; + compressed_size += 1; + *compressed++ = 0; + compressed_size += 1; + } + else + { + uiRunStartPosition = compressed++; + while((*data != 0) && (uiRunLength < 128) && (uiBufferPos < SIZE) && (scanline < info->width)) + { + *compressed++ = *data++; + uiRunLength++; + uiBufferPos++; + scanline++; + } + *uiRunStartPosition = uiRunLength | COMPRESS_NON_TRANSPARENT; + compressed_size += uiRunLength+1; + } + // prepare next run + uiRunLength = 0; + bZeroRun = (*data != 0) ? false : true; + if(scanline == info->width) + { + scanline = 0; + // "close" scanline with a zero + *compressed++ = 0; + compressed_size += 1; + } + if(uiBufferPos >= SIZE) + { + done = true; + } + } + UINT32 etrle_size = compressed_size; +#endif + //subimage.uiDataLength = compressed_size; + subimage.uiDataLength = etrle_size; + subimage.uiDataOffset = 0; + + hImage->ubBitDepth = 8; + //hImage->uiSizePixData = compressed_size; + hImage->uiSizePixData = etrle_size; + + hImage->usHeight = (UINT16)info->height; + hImage->usWidth = (UINT16)info->width; + + hImage->fFlags |= IMAGE_TRLECOMPRESSED; + hImage->fFlags |= IMAGE_BITMAPDATA; + + // palette + THROWIFFALSE(info->num_palette == 256, L"palette has size != 256"); + + hImage->pPalette = new SGPPaletteEntry[256]; + for(int i=0; i<256; ++i) + { + hImage->pPalette[i].peRed = info->palette[i].red; + hImage->pPalette[i].peGreen = info->palette[i].green; + hImage->pPalette[i].peBlue = info->palette[i].blue; + hImage->pPalette[i].peFlags = 0; + } + hImage->fFlags |= IMAGE_PALETTE; +} + diff --git a/Standard Gaming Platform/PngLoader.h b/Standard Gaming Platform/PngLoader.h new file mode 100644 index 00000000..c4631ce3 --- /dev/null +++ b/Standard Gaming Platform/PngLoader.h @@ -0,0 +1,13 @@ +#ifndef _PNGLOADER_H_ +#define _PNGLOADER_H_ + +#include "types.h" +#include "himage.h" + +bool LoadPNGFileToImage(HIMAGE hImage, UINT16 fContents); + +bool LoadJPCFileToImage(HIMAGE hImage, UINT16 fContents); + + +#endif // _PNGLOADER_H_ + diff --git a/Standard Gaming Platform/SGP_2005Express.vcproj b/Standard Gaming Platform/SGP_2005Express.vcproj index 6600ca6d..a8a7ce16 100644 --- a/Standard Gaming Platform/SGP_2005Express.vcproj +++ b/Standard Gaming Platform/SGP_2005Express.vcproj @@ -1,7 +1,7 @@ + + @@ -642,6 +646,10 @@ RelativePath=".\PCX.cpp" > + + diff --git a/Standard Gaming Platform/SGP_VS2008.vcproj b/Standard Gaming Platform/SGP_VS2008.vcproj index 2ca1e468..c624a0a6 100644 --- a/Standard Gaming Platform/SGP_VS2008.vcproj +++ b/Standard Gaming Platform/SGP_VS2008.vcproj @@ -423,6 +423,10 @@ RelativePath="pcx.h" > + + @@ -639,6 +643,10 @@ RelativePath="PCX.cpp" > + + diff --git a/Standard Gaming Platform/himage.cpp b/Standard Gaming Platform/himage.cpp index ffd797a9..6d6d3315 100644 --- a/Standard Gaming Platform/himage.cpp +++ b/Standard Gaming Platform/himage.cpp @@ -14,11 +14,18 @@ #include "impTGA.h" #include "pcx.h" #include "STCI.h" + #include "PngLoader.h" #include "wcheck.h" #include "Compression.h" #include "vobject.h" #endif +#include "VFS/vfs.h" + +const utf8string::str_t CONST_DOTJPC(L".jpc.7z"); + + + // This is the color substituted to keep a 24bpp->16bpp color // from going transparent (0x0000) -- DB @@ -79,19 +86,42 @@ HIMAGE CreateImage( SGPFILENAME ImageFile, UINT16 fContents ) iFileLoader = PCX_FILE_READER; break; } - - if ( _stricmp( Extension, "TGA" ) == 0 ) + else if ( _stricmp( Extension, "TGA" ) == 0 ) { iFileLoader = TGA_FILE_READER; break; } - - if ( _stricmp( Extension, "STI" ) == 0 ) + else if ( _stricmp( Extension, "STI" ) == 0 ) { +#ifdef USE_VFS + // see if there is a .jpc file first and when that fails, try .sti + utf8string str(ImageFile); + utf8string::str_t const& findext = str.c_wcs(); + utf8string::size_t dot = findext.find_last_of(vfs::Const::DOT()); + utf8string fname = findext.substr(0,dot).append(CONST_DOTJPC); + if(GetVFS()->FileExists(fname)) + { + iFileLoader = JPC_FILE_READER; + strncpy(ImageFile, fname.utf8().c_str(), fname.length()); + ImageFile[fname.length()] = 0; + break; + } +#endif iFileLoader = STCI_FILE_READER; break; } - + else if ( _stricmp( Extension, "PNG" ) == 0 ) + { + iFileLoader = PNG_FILE_READER; + break; + } +#ifdef USE_VFS + else if ( StrCmp::Equal(Extension, L"jpc.7z") ) + { + iFileLoader = JPC_FILE_READER; + break; + } +#endif } while ( FALSE ); // Determine if resource exists before creating image structure @@ -104,6 +134,7 @@ HIMAGE CreateImage( SGPFILENAME ImageFile, UINT16 fContents ) #endif #endif DbgMessage( TOPIC_HIMAGE, DBG_LEVEL_2, String("Resource file %s does not exist.", ImageFile) ); + return( NULL ); } @@ -221,6 +252,14 @@ BOOLEAN LoadImageData( HIMAGE hImage, UINT16 fContents ) fReturnVal = LoadSTCIFileToImage( hImage, fContents ); break; + case PNG_FILE_READER: + fReturnVal = LoadPNGFileToImage( hImage, fContents ); + break; + + case JPC_FILE_READER: + fReturnVal = LoadJPCFileToImage( hImage, fContents ); + break; + default: DbgMessage( TOPIC_HIMAGE, DBG_LEVEL_2, "Unknown image loader was specified." ); @@ -905,6 +944,10 @@ BOOLEAN GetETRLEImageData( HIMAGE hImage, ETRLEData *pBuffer ) // Create buffer for objects pBuffer->pETRLEObject = (ETRLEObject *) MemAlloc( sizeof( ETRLEObject ) * pBuffer->usNumberOfObjects ); + if(!pBuffer->pETRLEObject) + { + return false; + } CHECKF( pBuffer->pETRLEObject != NULL ); memset( pBuffer->pETRLEObject, 0, sizeof( ETRLEObject ) * pBuffer->usNumberOfObjects ); @@ -913,6 +956,10 @@ BOOLEAN GetETRLEImageData( HIMAGE hImage, ETRLEData *pBuffer ) // Allocate memory for pixel data pBuffer->pPixData = MemAlloc( hImage->uiSizePixData ); + if(!pBuffer->pPixData) + { + return false; + } CHECKF( pBuffer->pPixData != NULL ); memset( pBuffer->pPixData, 0, hImage->uiSizePixData ); diff --git a/Standard Gaming Platform/himage.h b/Standard Gaming Platform/himage.h index 72167b73..2396b23c 100644 --- a/Standard Gaming Platform/himage.h +++ b/Standard Gaming Platform/himage.h @@ -19,6 +19,8 @@ #define TGA_FILE_READER 0x2 #define STCI_FILE_READER 0x4 #define TRLE_FILE_READER 0x8 +#define PNG_FILE_READER 0x10 +#define JPC_FILE_READER 0x20 #define UNKNOWN_FILE_READER 0x200 // Defines for buffer bit depth @@ -93,41 +95,29 @@ typedef struct { UINT16 usWidth; UINT16 usHeight; - UINT8 ubBitDepth; + UINT8 ubBitDepth; UINT16 fFlags; - SGPFILENAME ImageFile; + SGPFILENAME ImageFile; UINT32 iFileLoader; - SGPPaletteEntry *pPalette; - UINT16 *pui16BPPPalette; - UINT8 * pAppData; + SGPPaletteEntry* pPalette; + UINT16* pui16BPPPalette; + UINT8* pAppData; UINT32 uiAppDataSize; // This union is used to describe each data type and is flexible to include the // data strucutre of the compresssed format, once developed. union { - //struct - //{ - PTR pImageData; - //}; - //struct - //{ - PTR pCompressedImageData; - //}; - //struct - //{ - UINT8 *p8BPPData; - //}; - //struct - //{ - - UINT16 *p16BPPData; - //}; + PTR pImageData; + PTR pCompressedImageData; + UINT8* p8BPPData; + UINT16* p16BPPData; + UINT32* p32BPPData; struct { - UINT8 * pPixData8; - UINT32 uiSizePixData; - ETRLEObject * pETRLEObject; - UINT16 usNumberOfObjects; + UINT8* pPixData8; + UINT32 uiSizePixData; + ETRLEObject* pETRLEObject; + UINT16 usNumberOfObjects; }; }; diff --git a/Standard Gaming Platform/mousesystem.cpp b/Standard Gaming Platform/mousesystem.cpp index 647025c9..2c4b75cc 100644 --- a/Standard Gaming Platform/mousesystem.cpp +++ b/Standard Gaming Platform/mousesystem.cpp @@ -1080,7 +1080,7 @@ void MSYS_DisableRegion(MOUSE_REGION *region) // void MSYS_SetCurrentCursor(UINT16 Cursor) { - SetCurrentCursorFromDatabase( Cursor ); + TRYCATCH_RETHROW(SetCurrentCursorFromDatabase( Cursor ), "Could not set Cursor"); } diff --git a/Standard Gaming Platform/sgp.cpp b/Standard Gaming Platform/sgp.cpp index 7bb4710d..6543b33a 100644 --- a/Standard Gaming Platform/sgp.cpp +++ b/Standard Gaming Platform/sgp.cpp @@ -37,6 +37,13 @@ #include "input.h" #include "zmouse.h" +#include "VFS/vfs.h" +#include "VFS/vfs_init.h" +#include "VFS/File/vfs_file.h" +#include "VFS/Location/vfs_slf_library.h" +#include "Text.h" + +#define USE_CONSOLE 0 #include @@ -46,6 +53,7 @@ #include "INIReader.h" #include "Console.h" #include "Lua Interpreter.h" +#include "connect.h" #ifdef JA2 #include "BuildDefines.h" @@ -57,6 +65,24 @@ #endif +static void MAGIC(std::string const& aarrrrgggh = "") +{} + +static bool s_VfsIsInitialized = false; +static std::list vfs_config_ini; + + +void SHOWEXCEPTION(CBasicException& ex) +{ + try { + _ExceptionMessage(ex); + } + catch(CBasicException &ex2) { + LogException(ex2); + exit(0); + } +} + extern UINT32 MemDebugCounter; #ifdef JA2 extern BOOLEAN gfPauseDueToPlayerGamePause; @@ -76,7 +102,9 @@ void GetRuntimeSettings( ); int PASCAL HandledWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCommandLine, int sCommandShow); +#if USE_CONSOLE Console g_Console("", "", "Lua Console", "no"); +#endif #if !defined(JA2) && !defined(UTILS) void ProcessCommandLine(CHAR8 *pCommandLine); @@ -485,12 +513,20 @@ INT32 FAR PASCAL WindowProcedure(HWND hWindow, UINT16 Message, WPARAM wParam, LP break; case WM_CHAR: - if (wParam == '\\' && - lParam && KF_ALTDOWN) { - g_Console.Create(ghWindow); - cout << "LUA console ready" << endl; - cout << "> "; + // WANNE: We disable this for now in multiplayer, because user could enter "\" for the file transfer path + if (!is_networked) + { + if (wParam == '\\' && + lParam && KF_ALTDOWN) + { +#if USE_CONSOLE + g_Console.Create(ghWindow); + cout << "LUA console ready" << endl; + cout << "> "; +#endif + } + } } break; @@ -515,8 +551,6 @@ INT32 FAR PASCAL WindowProcedure(HWND hWindow, UINT16 Message, WPARAM wParam, LP return 0L; } - - BOOLEAN InitializeStandardGamingPlatform(HINSTANCE hInstance, int sCommandShow) { FontTranslationTable *pFontTable; @@ -533,7 +567,31 @@ BOOLEAN InitializeStandardGamingPlatform(HINSTANCE hInstance, int sCommandShow) #endif // Second, read in settings - GetRuntimeSettings( ); + std::list exlist; + try + { + GetRuntimeSettings( ); + } + catch(CBasicException& ex) + { + // nothing is set up, no vfs, no video manager + // regular error processing wouldn't work here + // set default values and continue as if nothing has happened + iResolution = 1; + + gbPixelDepth = PIXEL_DEPTH; + + SCREEN_WIDTH = 800; + SCREEN_HEIGHT = 600; + + iScreenWidthOffset = (SCREEN_WIDTH - 640) / 2; + iScreenHeightOffset = (SCREEN_HEIGHT - 480) / 2; + + iScreenMode = 1; + iPlayIntro = 0; + // rethrow exception after full setup + exlist.push_back(ex); + } // Initialize the Debug Manager - success doesn't matter InitializeDebugManager(); @@ -603,12 +661,37 @@ BOOLEAN InitializeStandardGamingPlatform(HINSTANCE hInstance, int sCommandShow) return FALSE; } + InitializeJA2Clock(); + //InitializeJA2TimerID(); + +#ifdef USE_VFS + STRING512 sExecutableDir; + GetExecutableDirectory( sExecutableDir ); + + //vfs::Path fullpath = vfs::Path(sExecutableDir) + vfs::Path("vfs_config.ini"); + + // set current directory to exe's directory + SetCurrentDirectory(sExecutableDir); + + //if(!InitVirtualFileSystem( fullpath )) + //{ + // FastDebugMsg("FAILED : Initializing Virtual File System"); + // return FALSE; + //} + //THROWIFFALSE( InitVirtualFileSystem( fullpath ), L"Initializing Virtual File System failed"); + THROWIFFALSE( InitVirtualFileSystem( vfs_config_ini ), L"Initializing Virtual File System failed"); + + s_VfsIsInitialized = true; + + GetVFS()->GetVirtualLocation(vfs::Path("Temp"),true)->SetIsExclusive(true); + GetVFS()->GetVirtualLocation(vfs::Path("ShadeTables"),true)->SetIsExclusive(true); + GetVFS()->GetVirtualLocation(vfs::Path(pMessageStrings[MSG_SAVEDIRECTORY]+3),true)->SetIsExclusive(true); + GetVFS()->GetVirtualLocation(vfs::Path(pMessageStrings[MSG_MPSAVEDIRECTORY]+3),true)->SetIsExclusive(true); +#else // Snap: moved the following from InitJA2SplashScreen for clarity STRING512 CurrentDir; STRING512 DataDir; - InitializeJA2Clock(); - //InitializeJA2TimerID(); // Get Executable Directory GetExecutableDirectory( CurrentDir ); @@ -633,7 +716,7 @@ BOOLEAN InitializeStandardGamingPlatform(HINSTANCE hInstance, int sCommandShow) if ( GetPrivateProfileString( "Ja2 Settings","CUSTOM_DATA_LOCATION", "", customDataPath, MAX_PATH, "..\\Ja2.ini" ) ) { gCustomDataCat.NewCat(std::string(CurrentDir) + '\\' + customDataPath); } - +#endif //#ifdef JA2 InitJA2SplashScreen(); //#endif @@ -686,7 +769,11 @@ BOOLEAN InitializeStandardGamingPlatform(HINSTANCE hInstance, int sCommandShow) guiMouseWheelMsg = RegisterWindowMessage( MSH_MOUSEWHEEL ); gfGameInitialized = TRUE; - + + if(!exlist.empty()) + { + RETHROWEXCEPTION(L"Error during reading runtime settings", &exlist.front()); + } return TRUE; } @@ -762,6 +849,10 @@ void ShutdownStandardGamingPlatform(void) UnRegisterDebugTopic(TOPIC_SGP, "Standard Gaming Platform"); ShutdownDebugManager(); + + CLog::FlushFinally(); + vfs::CVirtualFileSystem::ShutdownVFS(); + CFileAllocator::Clear(); } @@ -870,10 +961,78 @@ int PASCAL HandledWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pC // ShowCursor(FALSE); - // Inititialize the SGP - if (InitializeStandardGamingPlatform(hInstance, sCommandShow) == FALSE) - { // We failed to initialize the SGP - return 0; +#ifdef USE_VFS + STRING512 sExecutableDir; + GetExecutableDirectory( sExecutableDir ); + SetCurrentDirectory(sExecutableDir); +#endif + try + { + // Inititialize the SGP + if (InitializeStandardGamingPlatform(hInstance, sCommandShow) == FALSE) + { + // We failed to initialize the SGP + return 0; + } + } + catch(CBasicException &ex) + { + if(!s_VfsIsInitialized) + { + vfs::CFile* fonts = new vfs::CFile("Data/Fonts.slf"); + vfs::CSLFLibrary* slfLib = new vfs::CSLFLibrary(vfs::tReadableFile::Cast(fonts),""); + if(slfLib->Init()) + { + GetVFS()->AddLocation(slfLib,"doesn't matter"); + } + // fonts not initialized + FontTranslationTable *pFontTable = CreateEnglishTransTable( ); + if ( pFontTable == NULL ) + { + return( FALSE ); + } + + // Initialize Font Manager + FastDebugMsg("Initializing the Font Manager"); + // Init the manager and copy the TransTable stuff into it. + if ( !InitializeFontManager( 8, pFontTable ) ) + { + FastDebugMsg("FAILED : Initializing Font Manager"); + return FALSE; + } + // Don't need this thing anymore, so get rid of it (but don't de-alloc the contents) + MemFree( pFontTable ); + if ( !InitializeFonts( ) ) + { + // Send debug message and quit + DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "COULD NOT INUT FONT SYSTEM..."); + return( ERROR_SCREEN ); + } + } + gfProgramIsRunning = 1; + LogException(ex); + SHOWEXCEPTION(ex); + } + catch(std::exception &ex) + { + gfProgramIsRunning = 1; + CBasicException nex(ex.what(),_FUNCTION_FORMAT_,__LINE__,__FILE__); + LogException(nex); + SHOWEXCEPTION(nex); + } + catch(const char* msg) + { + gfProgramIsRunning = 1; + CBasicException ex(msg,_FUNCTION_FORMAT_,__LINE__,__FILE__); + LogException(ex); + SHOWEXCEPTION(ex); + } + catch(...) + { + gfProgramIsRunning = 1; + CBasicException ex("Caught undefined exception", _FUNCTION_FORMAT_, __LINE__, __FILE__); + LogException( ex ); + SHOWEXCEPTION(ex); } #ifdef LUACONSOLE @@ -901,18 +1060,45 @@ int PASCAL HandledWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pC // attend to the gaming mechanics themselves Message.wParam = 0; - while (gfProgramIsRunning) + try { -// if (PeekMessage(&Message, NULL, 0, 0, PM_NOREMOVE)) -// { // We have a message on the WIN95 queue, let's get it - if (!GetMessage(&Message, NULL, 0, 0)) - { // It's quitting time - return Message.wParam; + MAGIC(); + while (gfProgramIsRunning) + { + if (!GetMessage(&Message, NULL, 0, 0)) + { + // It's quitting time + return Message.wParam; + } + // Ok, now that we have the message, let's handle it + TranslateMessage(&Message); + DispatchMessage(&Message); } - // Ok, now that we have the message, let's handle it - TranslateMessage(&Message); - DispatchMessage(&Message); } + catch(CBasicException &ex) + { + LogException(ex); + SHOWEXCEPTION(ex); + } + catch(std::exception &ex) + { + CBasicException nex(ex.what(),_FUNCTION_FORMAT_,__LINE__,__FILE__); + LogException(nex); + SHOWEXCEPTION(nex); + } + catch(const char* msg) + { + CBasicException ex(msg,_FUNCTION_FORMAT_,__LINE__,__FILE__); + LogException(ex); + SHOWEXCEPTION(ex); + } + catch(...) + { + CBasicException ex("Caught undefined exception", _FUNCTION_FORMAT_, __LINE__, __FILE__); + LogException( ex ); + SHOWEXCEPTION(ex); + } + #if 0 else { // Windows hasn't processed any messages, therefore we handle the rest @@ -992,6 +1178,7 @@ void SGPExit(void) void GetRuntimeSettings( ) { +#ifndef USE_VFS // Runtime settings - for now use INI file - later use registry STRING512 INIFile; // Path to the ini file CHAR8 zScreenResolution[ 50 ]; @@ -1000,19 +1187,43 @@ void GetRuntimeSettings( ) GetExecutableDirectory( INIFile ); strcat(INIFile, "\\Ja2.ini"); - +#else + CPropertyContainer oProps; + oProps.InitFromIniFile("Ja2.ini"); +#endif iResolution = -1; - +#ifndef USE_VFS if (GetPrivateProfileString( "Ja2 Settings","SCREEN_RESOLUTION", "", zScreenResolution, 50, INIFile )) { iResolution = atoi(zScreenResolution); } +#else + iResolution = oProps.GetIntProperty(L"Ja2 Settings", L"SCREEN_RESOLUTION", -1); + + std::list ini_list; + if(oProps.GetStringListProperty(L"Ja2 Settings", L"VFS_CONFIG_INI", ini_list, L"")) + { + vfs_config_ini.clear(); + for(std::list::iterator it = ini_list.begin(); it != ini_list.end(); ++it) + { + vfs_config_ini.push_back(*it); + } + } + else + { + vfs_config_ini.push_back(L"vfs_config.ini"); + } +#endif #ifdef JA2EDITOR +#ifndef USE_VFS if (GetPrivateProfileString( "Ja2 Settings","EDITOR_SCREEN_RESOLUTION", "", zScreenResolution, 50, INIFile )) { iResolution = atoi(zScreenResolution); } +#else + iResolution = oProps.GetIntProperty("Ja2 Settings","EDITOR_SCREEN_RESOLUTION", -1); +#endif #endif @@ -1038,7 +1249,7 @@ void GetRuntimeSettings( ) iResY = 600; break; } - +#ifndef USE_VFS gbPixelDepth = GetPrivateProfileInt( "SGP", "PIXEL_DEPTH", PIXEL_DEPTH, INIFile ); SCREEN_WIDTH = GetPrivateProfileInt( "SGP", "WIDTH", iResX, INIFile ); @@ -1054,6 +1265,25 @@ void GetRuntimeSettings( ) // WANNE: Should we play the intro? iPlayIntro = (int) GetPrivateProfileInt( "Ja2 Settings","PLAY_INTRO", iPlayIntro, INIFile ); +#else + gbPixelDepth = (UINT8)oProps.GetIntProperty(L"SGP", L"PIXEL_DEPTH", PIXEL_DEPTH); + + SCREEN_WIDTH = (UINT16)oProps.GetIntProperty(L"SGP", L"WIDTH", iResX); + SCREEN_HEIGHT = (UINT16)oProps.GetIntProperty(L"SGP", L"HEIGHT", iResY); + + iScreenWidthOffset = (SCREEN_WIDTH - 640) / 2; + iScreenHeightOffset = (SCREEN_HEIGHT - 480) / 2; + + /* Sergeant_Kolja. 2007-02-20: runtime Windowed mode instead of compile-time */ + /* 1 for Windowed, 0 for Fullscreen */ + if( !bScreenModeCmdLine ) + { + iScreenMode = oProps.GetIntProperty("Ja2 Settings","SCREEN_MODE_WINDOWED", iScreenMode); + } + + // WANNE: Should we play the intro? + iPlayIntro = oProps.GetIntProperty("Ja2 Settings","PLAY_INTRO", iPlayIntro); +#endif } void ShutdownWithErrorBox(CHAR8 *pcMessage) diff --git a/Standard Gaming Platform/soundman.cpp b/Standard Gaming Platform/soundman.cpp index e9f6c802..eb7d65aa 100644 --- a/Standard Gaming Platform/soundman.cpp +++ b/Standard Gaming Platform/soundman.cpp @@ -1801,10 +1801,14 @@ UINT32 uiCount; return(FALSE); } +#include "vfs.h" +#include "PropertyContainer.h" // Lesh modifications // Sound debug - +#ifdef USE_VFS +static CLog& s_SoundLog = *CLog::Create(SndDebugFileName,true); +#endif //***************************************************************************************** // SoundLog // Writes string into log file @@ -1815,11 +1819,15 @@ UINT32 uiCount; //***************************************************************************************** void SoundLog(CHAR8 *strMessage) { +#ifndef USE_VFS if ((SndDebug = fopen(SndDebugFileName, "a+t")) != NULL) { fprintf(SndDebug, "%s\n", strMessage); fclose(SndDebug); } +#else + s_SoundLog << strMessage << CLog::endl; +#endif } //***************************************************************************************** @@ -1832,8 +1840,10 @@ void SoundLog(CHAR8 *strMessage) //***************************************************************************************** void InitLogging() { +#ifndef USE_VFS if ((SndDebug = fopen(SndDebugFileName, "wt")) != NULL) { fclose(SndDebug); } +#endif } diff --git a/Standard Gaming Platform/video.cpp b/Standard Gaming Platform/video.cpp index f408450d..6e8969ab 100644 --- a/Standard Gaming Platform/video.cpp +++ b/Standard Gaming Platform/video.cpp @@ -2916,7 +2916,11 @@ BOOLEAN EraseMouseCursor( ) // pTmpPointer = LockMouseBuffer(&uiPitch); - memset(pTmpPointer, 0, MAX_CURSOR_HEIGHT * uiPitch); + // when there is no mouse buffer the game can run into an infinite loop (some DirectX stuff) + if(pTmpPointer) + { + memset(pTmpPointer, 0, MAX_CURSOR_HEIGHT * uiPitch); + } UnlockMouseBuffer(); // Don't set dirty diff --git a/Standard Gaming Platform/vobject.cpp b/Standard Gaming Platform/vobject.cpp index 25765140..c393ee67 100644 --- a/Standard Gaming Platform/vobject.cpp +++ b/Standard Gaming Platform/vobject.cpp @@ -455,6 +455,54 @@ HVOBJECT CreateVideoObject( VOBJECT_DESC *VObjectDesc ) } } + if(hImage->ubBitDepth == 32) + { + hVObject->p16BPPObject = new SixteenBPPObjectInfo; + int SIZE = hImage->usHeight*hImage->usWidth; + hVObject->pPixData = new UINT32[SIZE]; + memcpy(hVObject->pPixData, hImage->p32BPPData, SIZE*sizeof(UINT32)); + hVObject->p16BPPObject->p16BPPData = NULL; + hVObject->p16BPPObject->sOffsetX = 0; + hVObject->p16BPPObject->sOffsetY = 0; + hVObject->p16BPPObject->ubShadeLevel = 0; + hVObject->p16BPPObject->usHeight = hImage->usHeight; + hVObject->p16BPPObject->usWidth = hImage->usWidth; + hVObject->p16BPPObject->usRegionIndex = 0; + + hVObject->usNumberOf16BPPObjects = 1; + hVObject->ubBitDepth = hImage->ubBitDepth; + + if ( VObjectDesc->fCreateFlags & VOBJECT_CREATE_FROMFILE ) + { + DestroyImage( hImage ); + } + + return hVObject; + } + else if(hImage->ubBitDepth == 16) + { + hVObject->p16BPPObject = new SixteenBPPObjectInfo; + int SIZE = hImage->usHeight*hImage->usWidth; + hVObject->p16BPPObject->p16BPPData = new UINT16[SIZE]; + memcpy(hVObject->p16BPPObject->p16BPPData, hImage->p16BPPData, SIZE*sizeof(UINT16)); + hVObject->p16BPPObject->sOffsetX = 0; + hVObject->p16BPPObject->sOffsetY = 0; + hVObject->p16BPPObject->ubShadeLevel = 0; + hVObject->p16BPPObject->usHeight = hImage->usHeight; + hVObject->p16BPPObject->usWidth = hImage->usWidth; + hVObject->p16BPPObject->usRegionIndex = 0; + + hVObject->usNumberOf16BPPObjects = 1; + hVObject->ubBitDepth = hImage->ubBitDepth; + + if ( VObjectDesc->fCreateFlags & VOBJECT_CREATE_FROMFILE ) + { + DestroyImage( hImage ); + } + + return hVObject; + } + // Check if returned himage is TRLE compressed - return error if not if ( ! (hImage->fFlags & IMAGE_TRLECOMPRESSED ) ) { @@ -723,23 +771,49 @@ BOOLEAN BltVideoObjectToBuffer( UINT16 *pBuffer, UINT32 uiDestPitchBYTES, HVOBJE __try { // Assertions - Assert( pBuffer != NULL ); + //Assert( pBuffer != NULL ); + THROWIFFALSE( pBuffer != NULL, L"No Destination Buffer" ); if ( hSrcVObject == NULL ) { //int breakpoint=0; } - Assert( hSrcVObject != NULL ); + //Assert( hSrcVObject != NULL ); + THROWIFFALSE( hSrcVObject != NULL, L"No Source Object" ) + SixteenBPPObjectInfo *image = NULL; // Check For Flags and bit depths switch( hSrcVObject->ubBitDepth ) { + case 32: + image = &hSrcVObject->p16BPPObject[usIndex]; +#if 0 + Blt16BPPTo16BPPTrans( pBuffer, uiDestPitchBYTES, + image->p16BPPData, image->usWidth * sizeof(UINT16), + iDestX, iDestY, + 0, 0, image->usWidth, image->usHeight, + 0 ); +#else + Blt32BPPTo16BPPTrans( pBuffer, uiDestPitchBYTES, + (UINT32*)hSrcVObject->pPixData, image->usWidth * sizeof(UINT32), + iDestX, iDestY, + 0, 0, image->usWidth, image->usHeight); +#endif + break; + case 16: + image = &hSrcVObject->p16BPPObject[usIndex]; + Blt16BPPTo16BPP( pBuffer, uiDestPitchBYTES, + image->p16BPPData, image->usWidth * sizeof(UINT16), + iDestX, iDestY, + 0, 0, image->usWidth, image->usHeight ); + break; case 8: + THROWIFFALSE( hSrcVObject->usNumberOfObjects > usIndex, L"Video object index is larger than the number of subimages"); // Switch based on flags given do { diff --git a/Standard Gaming Platform/vobject_blitters.cpp b/Standard Gaming Platform/vobject_blitters.cpp index 91a01976..6c84db92 100644 --- a/Standard Gaming Platform/vobject_blitters.cpp +++ b/Standard Gaming Platform/vobject_blitters.cpp @@ -22,6 +22,85 @@ #endif +static UINT8 g_AlphaTimesValueCache[256][256]; + +class InitAlphaTimesValueCache +{ +public: + InitAlphaTimesValueCache() + { + for(unsigned int alpha = 0; alpha < 256; alpha++) + { + for(unsigned int val = 0; val < 256; val++) + { + // it can't get any simpler than that + g_AlphaTimesValueCache[alpha][val] = (UINT8)( ((double)alpha/255.0) * val ); + } + } + } +}; + +static InitAlphaTimesValueCache s_InitAlphaCache; + +BOOLEAN Blt32BPPTo16BPPTrans(UINT16 *pDest, UINT32 uiDestPitch, UINT32 *pSrc, UINT32 uiSrcPitch, INT32 iDestXPos, INT32 iDestYPos, INT32 iSrcXPos, INT32 iSrcYPos, UINT32 uiWidth, UINT32 uiHeight) +{ + UINT32 *pSrcPtr; + UINT16 *pDestPtr; + UINT32 uiLineSkipDest, uiLineSkipSrc; + + Assert(pDest!=NULL); + Assert(pSrc!=NULL); + + pSrcPtr = (UINT32 *)((UINT8 *)pSrc+(iSrcYPos*uiSrcPitch)+(iSrcXPos*4)); + uiLineSkipSrc = uiSrcPitch-(uiWidth*4); + + pDestPtr = (UINT16 *)((UINT8 *)pDest+(iDestYPos*uiDestPitch)+(iDestXPos*2)); + uiLineSkipDest = uiDestPitch-(uiWidth*2); + + UINT8 alpha, dst_channel, src_channel; + UINT8 red, green, blue; + do + { + UINT32 w = uiWidth; + do + { + //alpha = (UINT8)((0xFF000000 & *pSrcPtr) >> 24); + alpha = 255; + // r + dst_channel = (UINT8)((0x1F & *pDestPtr) << 3); + src_channel = (UINT8)((0xFF & *pSrcPtr) ); + //red = (UINT8)( g_AlphaTimesValueCache[255-alpha][dst_channel] + g_AlphaTimesValueCache[alpha][src_channel] ); + red = (UINT8)( g_AlphaTimesValueCache[alpha][src_channel] ); + + // g + dst_channel = (UINT8)((0x7E0 & *pDestPtr) >> 3); + src_channel = (UINT8)((0xFF00 & *pSrcPtr) >> 8); + //green = (UINT8)( g_AlphaTimesValueCache[255-alpha][dst_channel] + g_AlphaTimesValueCache[alpha][src_channel] ); + green = (UINT8)( g_AlphaTimesValueCache[alpha][src_channel] ); + + // b + dst_channel = (UINT8)((0xF800 & *pDestPtr) >> 8); + src_channel = (UINT8)((0xFF0000 & *pSrcPtr) >> 16); + //blue = (UINT8)( g_AlphaTimesValueCache[255-alpha][dst_channel] + g_AlphaTimesValueCache[alpha][src_channel] ); + blue = (UINT8)( g_AlphaTimesValueCache[alpha][src_channel] ); + + + UINT32 newcolor = FROMRGB(red,green,blue); + *pDestPtr = Get16BPPColor(newcolor); + pSrcPtr++; + pDestPtr++; + } + while (--w != 0); + pSrcPtr = (UINT32*)((UINT8*)pSrcPtr + uiLineSkipSrc); + pDestPtr = (UINT16*)((UINT8*)pDestPtr + uiLineSkipDest); + } + while (--uiHeight != 0); + + return ( TRUE ); +} + + + /* Here are bliting functions. If you dont know what kind of functions they are so : * correct me if im wrong they copy array of bits from src image to dest image * maby we can get ride this includes above? we dont need theme here i thinks so @@ -6907,6 +6986,15 @@ UINT32 uiLineSkipDest, uiLineSkipSrc; Assert(pDest!=NULL); Assert(pSrc!=NULL); + if(iSrcXPos >= SCREEN_WIDTH) + return false; + if(iSrcYPos >= SCREEN_HEIGHT) + return false; + if(iSrcXPos + uiWidth > SCREEN_WIDTH) + uiWidth = SCREEN_WIDTH - iSrcXPos; + if(iSrcYPos + uiHeight > SCREEN_HEIGHT) + uiHeight = SCREEN_HEIGHT - iSrcYPos; + pSrcPtr=(UINT16 *)((UINT8 *)pSrc+(iSrcYPos*uiSrcPitch)+(iSrcXPos*2)); pDestPtr=(UINT16 *)((UINT8 *)pDest+(iDestYPos*uiDestPitch)+(iDestXPos*2)); uiLineSkipDest=uiDestPitch-(uiWidth*2); @@ -6978,7 +7066,22 @@ UINT32 uiLineSkipDest, uiLineSkipSrc; pDestPtr=(UINT16 *)((UINT8 *)pDest+(iDestYPos*uiDestPitch)+(iDestXPos*2)); uiLineSkipDest=uiDestPitch-(uiWidth*2); uiLineSkipSrc=uiSrcPitch-(uiWidth*2); - +#if 1 + do + { + UINT32 w = uiWidth; + do + { + if (*pSrcPtr != usTrans) *pDestPtr = *pSrcPtr; + pSrcPtr++; + pDestPtr++; + } + while (--w != 0); + pSrcPtr = (UINT16*)((UINT8*)pSrcPtr + uiLineSkipSrc); + pDestPtr = (UINT16*)((UINT8*)pDestPtr + uiLineSkipDest); + } + while (--uiHeight != 0); +#else __asm { mov esi, pSrcPtr mov edi, pDestPtr @@ -7007,7 +7110,7 @@ Blit3: jnz BlitNewLine } - +#endif return(TRUE); } diff --git a/Standard Gaming Platform/vobject_blitters.h b/Standard Gaming Platform/vobject_blitters.h index 636eab1f..1c977bea 100644 --- a/Standard Gaming Platform/vobject_blitters.h +++ b/Standard Gaming Platform/vobject_blitters.h @@ -162,6 +162,8 @@ BOOLEAN BlitZRect(UINT16 *pZBuffer, UINT32 uiPitch, INT16 sLeft, INT16 sTop, INT // New 16/16 blitters +BOOLEAN Blt32BPPTo16BPPTrans(UINT16 *pDest, UINT32 uiDestPitch, UINT32 *pSrc, UINT32 uiSrcPitch, INT32 iDestXPos, INT32 iDestYPos, INT32 iSrcXPos, INT32 iSrcYPos, UINT32 uiWidth, UINT32 uiHeight); + BOOLEAN Blt16BPPDataTo16BPPBufferTransparentClip( UINT16 *pBuffer, UINT32 uiDestPitchBYTES, HVOBJECT hSrcVObject, INT32 iX, INT32 iY, UINT16 usIndex, SGPRect *clipregion); BOOLEAN Blt16BPPDataTo16BPPBufferTransZClip( UINT16 *pBuffer, UINT32 uiDestPitchBYTES, UINT16 *pZBuffer, UINT16 usZValue, HVOBJECT hSrcVObject, INT32 iX, INT32 iY, UINT16 usIndex, SGPRect *clipregion); BOOLEAN Blt16BPPDataTo16BPPBufferTransparent( UINT16 *pBuffer, UINT32 uiDestPitchBYTES, HVOBJECT hSrcVObject, INT32 iX, INT32 iY, UINT16 usIndex ); diff --git a/Strategic/Assignments.cpp b/Strategic/Assignments.cpp index a996a703..12ea89b1 100644 --- a/Strategic/Assignments.cpp +++ b/Strategic/Assignments.cpp @@ -2057,7 +2057,7 @@ void VerifyTownTrainingIsPaidFor( void ) SOLDIERTYPE *pSoldier = NULL; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { // valid character? if( gCharactersList[ iCounter ].fValid == FALSE ) @@ -3950,9 +3950,9 @@ INT16 GetBonusTrainingPtsDueToInstructor( SOLDIERTYPE *pInstructor, SOLDIERTYPE // factor in their mutual relationship if (OKToCheckOpinion(pInstructor->ubProfile)) - bOpinionFactor = gMercProfiles[ pStudent->ubProfile ].bMercOpinion[ pInstructor->ubProfile ]; + bOpinionFactor = gMercProfiles[ pStudent->ubProfile ].bMercOpinion[ pInstructor->ubProfile ]; if (OKToCheckOpinion(pStudent->ubProfile)) - bOpinionFactor += gMercProfiles[ pInstructor->ubProfile ].bMercOpinion[ pStudent->ubProfile ] / 2; + bOpinionFactor += gMercProfiles[ pInstructor->ubProfile ].bMercOpinion[ pStudent->ubProfile ] / 2; } @@ -10663,7 +10663,7 @@ BOOLEAN HandleSelectedMercsBeingPutAsleep( BOOLEAN fWakeUp, BOOLEAN fDisplayWarn CHAR16 sString[ 128 ]; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { pSoldier = NULL; @@ -10982,7 +10982,7 @@ void SetAssignmentForList( INT8 bAssignment, INT8 bParam ) // sets assignment for the list - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( ( gCharactersList[ iCounter ].fValid ) && ( fSelectedListOfMercsForMapScreen[ iCounter ] == TRUE ) && @@ -11542,7 +11542,7 @@ SOLDIERTYPE *GetSelectedAssignSoldier( BOOLEAN fNullOK ) if ( (guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) ) { // mapscreen version - if( ( bSelectedAssignChar >= 0 ) && ( bSelectedAssignChar < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) && + if( ( bSelectedAssignChar >= 0 ) && ( bSelectedAssignChar < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) && ( gCharactersList[ bSelectedAssignChar ].fValid ) ) { pSoldier = &Menptr[ gCharactersList[ bSelectedAssignChar ].usSolID ]; diff --git a/Strategic/Game Event Hook.cpp b/Strategic/Game Event Hook.cpp index e3afedb6..cae080a6 100644 --- a/Strategic/Game Event Hook.cpp +++ b/Strategic/Game Event Hook.cpp @@ -87,6 +87,8 @@ BOOLEAN ExecuteStrategicEvent( STRATEGICEVENT *pEvent ) { BOOLEAN bMercDayOne = FALSE; +// BF : file access is bad, especially if a function is called so often. +#if 0 // Kaiden: Opening the INI File CIniReader iniReader("..\\Ja2_Options.ini"); @@ -96,7 +98,7 @@ BOOLEAN ExecuteStrategicEvent( STRATEGICEVENT *pEvent ) // for some reason, this can't be in gamesettings.cpp // or it won't work. bMercDayOne = iniReader.ReadBoolean("Options","MERC_DAY_ONE",FALSE); - +#endif DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"ExecuteStrategicEvent"); if( gGameExternalOptions.gfEnableEmergencyButton_SkipStrategicEvents && _KeyDown( NUM_LOCK ) ) diff --git a/Strategic/Game Init.cpp b/Strategic/Game Init.cpp index 70bad269..ba511d0c 100644 --- a/Strategic/Game Init.cpp +++ b/Strategic/Game Init.cpp @@ -519,10 +519,14 @@ BOOLEAN InitNewGame( BOOLEAN fReset ) { SetLaptopExitScreen( MAP_SCREEN ); //hayden SetPendingNewScreen( MAP_SCREEN ); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, L"------------------------------------------------------"); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[0]); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, L"------------------------------------------------------"); - ScreenMsg( MSG_FONT_WHITE, MSG_CHAT, MPHelp[1]); + + // WANNE: No more used + /* + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, L"------------------------------------------------------"); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[0]); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, L"------------------------------------------------------"); + ScreenMsg( MSG_FONT_WHITE, MSG_MPSYSTEM, MPHelp[1]); + */ } else @@ -698,7 +702,7 @@ void QuickSetupOfMercProfileItems( UINT32 uiCount, UINT8 ubProfileIndex ) // TEMP! // make carman's opinion of us high! if (OKToCheckOpinion(ubProfileIndex)) - gMercProfiles[ 78 ].bMercOpinion[ ubProfileIndex ] = 25; + gMercProfiles[ 78 ].bMercOpinion[ ubProfileIndex ] = 25; } else if ( uiCount == 1 ) @@ -896,5 +900,9 @@ void ReStartingGame() // Reset timer callbacks gpCustomizableTimerCallback = NULL; - gubCheatLevel = STARTING_CHEAT_LEVEL; + // WANNE - MP: Set cheat level to 0 for mp + if (!is_networked) + gubCheatLevel = STARTING_CHEAT_LEVEL; + else + gubCheatLevel = 0; } \ No newline at end of file diff --git a/Strategic/Map Screen Interface.cpp b/Strategic/Map Screen Interface.cpp index 01439400..2614cd07 100644 --- a/Strategic/Map Screen Interface.cpp +++ b/Strategic/Map Screen Interface.cpp @@ -432,7 +432,7 @@ void InitalizeVehicleAndCharacterList( void ) void SetEntryInSelectedCharacterList( INT8 bEntry ) { - Assert( ( bEntry >= 0 ) && ( bEntry < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ); + Assert( ( bEntry >= 0 ) && ( bEntry < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ); // set this entry to selected fSelectedListOfMercsForMapScreen[ bEntry ] = TRUE ; @@ -442,7 +442,7 @@ void SetEntryInSelectedCharacterList( INT8 bEntry ) void ResetEntryForSelectedList( INT8 bEntry ) { - Assert( ( bEntry >= 0 ) && ( bEntry < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ); + Assert( ( bEntry >= 0 ) && ( bEntry < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ); // set this entry to selected fSelectedListOfMercsForMapScreen[ bEntry ] = FALSE; @@ -469,7 +469,7 @@ void ResetSelectedListForMapScreen( void ) BOOLEAN IsEntryInSelectedListSet( INT8 bEntry ) { - Assert( ( bEntry >= 0 ) && ( bEntry < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ); + Assert( ( bEntry >= 0 ) && ( bEntry < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ); // is this entry in the selected list set? @@ -480,7 +480,7 @@ BOOLEAN IsEntryInSelectedListSet( INT8 bEntry ) void ToggleEntryInSelectedList( INT8 bEntry ) { - Assert( ( bEntry >= 0 ) && ( bEntry < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ); + Assert( ( bEntry >= 0 ) && ( bEntry < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ); // toggle the value in the selected list fSelectedListOfMercsForMapScreen[ bEntry ] = !( fSelectedListOfMercsForMapScreen[ bEntry ] ); @@ -522,7 +522,7 @@ BOOLEAN MultipleCharacterListEntriesSelected( void ) INT32 iCounter = 0; // check if more than one person is selected in the selected list - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( fSelectedListOfMercsForMapScreen[iCounter] == TRUE ) { @@ -547,7 +547,7 @@ void ResetAssignmentsForMercsTrainingUnpaidSectorsInSelectedList( INT8 bAssignme INT32 iCounter = 0; SOLDIERTYPE *pSoldier = NULL; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { // valid character? if( gCharactersList[ iCounter ].fValid == FALSE ) @@ -579,7 +579,7 @@ void ResetAssignmentOfMercsThatWereTrainingMilitiaInThisSector( INT16 sSectorX, INT32 iCounter = 0; SOLDIERTYPE *pSoldier = NULL; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { // valid character? if( gCharactersList[ iCounter ].fValid == FALSE ) @@ -632,7 +632,7 @@ void DeselectSelectedListMercsWhoCantMoveWithThisGuy( SOLDIERTYPE *pSoldier ) // deselect any other selected mercs that can't travel together with pSoldier - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[ iCounter ].fValid == TRUE ) { @@ -727,7 +727,7 @@ void SelectUnselectedMercsWhoMustMoveWithThisGuy( void ) SOLDIERTYPE *pSoldier = NULL; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[ iCounter ].fValid == TRUE ) { @@ -759,7 +759,7 @@ BOOLEAN AnyMercInSameSquadOrVehicleIsSelected( SOLDIERTYPE *pSoldier ) SOLDIERTYPE *pSoldier2 = NULL; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[ iCounter ].fValid == TRUE ) { @@ -838,7 +838,7 @@ void RestoreBackgroundForAssignmentGlowRegionList( void ) if( iOldAssignmentLine != giAssignHighLine ) { // restore background - RestoreExternBackgroundRect( 66, Y_START - 1, 118 + 1 - 67, ( INT16 )( ( ( CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1 ) * ( Y_SIZE + 2 ) ) + 1 ) ); + RestoreExternBackgroundRect( 66, Y_START - 1, 118 + 1 - 67, ( INT16 )( ( ( giMAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1 ) * ( Y_SIZE + 2 ) ) + 1 ) ); // ARM: not good enough! must reblit the whole panel to erase glow chunk restored by help text disappearing!!! fTeamPanelDirty = TRUE; @@ -866,7 +866,7 @@ void RestoreBackgroundForDestinationGlowRegionList( void ) if( iOldDestinationLine != giDestHighLine ) { // restore background - RestoreExternBackgroundRect( 182, Y_START - 1, 217 + 1 - 182, ( INT16 )( ( ( CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1 ) * ( Y_SIZE + 2 ) ) + 1 ) ); + RestoreExternBackgroundRect( 182, Y_START - 1, 217 + 1 - 182, ( INT16 )( ( ( giMAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1 ) * ( Y_SIZE + 2 ) ) + 1 ) ); // ARM: not good enough! must reblit the whole panel to erase glow chunk restored by help text disappearing!!! fTeamPanelDirty = TRUE; @@ -894,7 +894,7 @@ void RestoreBackgroundForContractGlowRegionList( void ) if( iOldContractLine != giContractHighLine ) { // restore background - RestoreExternBackgroundRect( 222, Y_START - 1, 250 + 1 - 222, ( INT16 )( ( ( CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1 ) * ( Y_SIZE + 2 ) ) + 1 ) ) ; + RestoreExternBackgroundRect( 222, Y_START - 1, 250 + 1 - 222, ( INT16 )( ( ( giMAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1 ) * ( Y_SIZE + 2 ) ) + 1 ) ) ; // ARM: not good enough! must reblit the whole panel to erase glow chunk restored by help text disappearing!!! fTeamPanelDirty = TRUE; @@ -926,7 +926,7 @@ void RestoreBackgroundForSleepGlowRegionList( void ) if( iOldSleepHighLine != giSleepHighLine ) { // restore background - RestoreExternBackgroundRect( 123, Y_START - 1, 142 + 1 - 123, ( INT16 )( ( ( CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1 ) * ( Y_SIZE + 2 ) ) + 1 ) ) ; + RestoreExternBackgroundRect( 123, Y_START - 1, 142 + 1 - 123, ( INT16 )( ( ( giMAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1 ) * ( Y_SIZE + 2 ) ) + 1 ) ) ; // ARM: not good enough! must reblit the whole panel to erase glow chunk restored by help text disappearing!!! fTeamPanelDirty = TRUE; @@ -967,7 +967,7 @@ void PlayGlowRegionSound( void ) INT16 CharacterIsGettingPathPlotted( INT16 sCharNumber ) { // valid character number? - if( ( sCharNumber < 0 ) || ( sCharNumber >= CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ) + if( ( sCharNumber < 0 ) || ( sCharNumber >= giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ) { return( FALSE ); } @@ -1009,7 +1009,7 @@ INT16 CharacterIsGettingPathPlotted( INT16 sCharNumber ) BOOLEAN IsCharacterSelectedForAssignment( INT16 sCharNumber ) { // valid character number? - if( ( sCharNumber < 0 ) || ( sCharNumber >= CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ) + if( ( sCharNumber < 0 ) || ( sCharNumber >= giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ) { return( FALSE ); } @@ -1046,7 +1046,7 @@ BOOLEAN IsCharacterSelectedForAssignment( INT16 sCharNumber ) BOOLEAN IsCharacterSelectedForSleep( INT16 sCharNumber ) { // valid character number? - if( ( sCharNumber < 0 ) || ( sCharNumber >= CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ) + if( ( sCharNumber < 0 ) || ( sCharNumber >= giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ) { return( FALSE ); } @@ -1249,7 +1249,7 @@ void CheckAndUpdateBasedOnContractTimes( void ) INT32 iCounter = 0; INT32 iTimeRemaining = 0; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[iCounter].fValid == TRUE ) { @@ -1345,7 +1345,7 @@ void HandleDisplayOfSelectedMercArrows( void ) BltVideoObject( guiSAVEBUFFER , hHandle, 0,SELECTED_CHAR_ARROW_X, sYPosition , VO_BLT_SRCTRANSPARENCY,NULL ); // now run through the selected list of guys, an arrow for each - for( ubCount = 0; ubCount < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; ubCount++ ) + for( ubCount = 0; ubCount < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; ubCount++ ) { if( gCharactersList[ ubCount ].fValid == TRUE ) { @@ -2111,7 +2111,7 @@ void FindAndSetThisContractSoldier( SOLDIERTYPE *pSoldier ) fShowContractMenu = FALSE; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[ iCounter].fValid == TRUE ) { @@ -2345,7 +2345,7 @@ INT32 GetNumberOfPeopleInCharacterList( void ) INT32 iCounter = 0, iCount = 0; // get the number of valid mercs in the mapscreen character list - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[ iCounter].fValid == TRUE ) { @@ -2513,7 +2513,7 @@ void GoToNextCharacterInList( void ) } // is the current guy invalid or the first one? - if( ( bSelectedInfoChar == -1 )|| ( bSelectedInfoChar == CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ) + if( ( bSelectedInfoChar == -1 )|| ( bSelectedInfoChar == giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ) { iCount = 0; } @@ -2522,9 +2522,9 @@ void GoToNextCharacterInList( void ) iCount = bSelectedInfoChar + 1; } - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { - if ( ( gCharactersList[ iCount ].fValid ) && ( iCount < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) && ValidSelectableCharForNextOrPrev( iCount ) ) + if ( ( gCharactersList[ iCount ].fValid ) && ( iCount < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) && ValidSelectableCharForNextOrPrev( iCount ) ) { ChangeSelectedInfoChar( ( INT8 )iCount, TRUE ); break; @@ -2533,7 +2533,7 @@ void GoToNextCharacterInList( void ) { iCount++; - if( iCount >= CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) + if( iCount >= giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) { iCount = 0; } @@ -2560,7 +2560,7 @@ void GoToPrevCharacterInList( void ) // is the current guy invalid or the first one? if( ( bSelectedInfoChar == -1 ) || ( bSelectedInfoChar == 0 ) ) { - iCount = CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; + iCount = giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; } else { @@ -2568,9 +2568,9 @@ void GoToPrevCharacterInList( void ) } // now run through the list and find first prev guy - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { - if ( ( gCharactersList[ iCount ].fValid ) && ( iCount < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) && ValidSelectableCharForNextOrPrev( iCount ) ) + if ( ( gCharactersList[ iCount ].fValid ) && ( iCount < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) && ValidSelectableCharForNextOrPrev( iCount ) ) { ChangeSelectedInfoChar( ( INT8 )iCount, TRUE ); break; @@ -2582,7 +2582,7 @@ void GoToPrevCharacterInList( void ) if( iCount < 0 ) { // was FIRST_VEHICLE - iCount = CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; + iCount = giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; } } } @@ -3363,7 +3363,7 @@ void AddSoldierToMovingLists( SOLDIERTYPE *pSoldier ) { INT32 iCounter = 0; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( pSoldierMovingList[ iCounter ] == pSoldier ) { @@ -3459,7 +3459,7 @@ void InitializeMovingLists( void ) giNumberOfVehiclesInSectorMoving = 0; // init the soldiers - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { // soldier is NOT moving pSoldierMovingList[ iCounter ] = NULL; @@ -3497,7 +3497,7 @@ BOOLEAN IsAnythingSelectedForMoving( void ) // check soldiers - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if ( ( pSoldierMovingList[ iCounter ] != NULL ) && fSoldierIsMoving[ iCounter ] ) { @@ -3580,7 +3580,7 @@ void SetUpMovingListsForSector( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ ) // note that Skyrider can't be moved using the move box, and won't appear because the helicoprer is not in the char list - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[ iCounter ].fValid ) { @@ -4442,7 +4442,7 @@ void HandleSettingTheSelectedListOfMercs( void ) bSelectedDestChar = -1; // run through the list of grunts - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { // is the current guy a valid character? if( gCharactersList[ iCounter ].fValid == TRUE ) @@ -6107,7 +6107,7 @@ BOOLEAN CanEntireMovementGroupMercIsInMove( SOLDIERTYPE *pSoldier, INT8 *pbError // even if group is 0 (not that that should happen, should it?) still loop through for other mercs selected to move // if anyone in the merc's group or also selected cannot move for whatever reason return false - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[ iCounter ].fValid == TRUE ) { diff --git a/Strategic/Merc Contract.cpp b/Strategic/Merc Contract.cpp index 4dba4788..7c9b08f7 100644 --- a/Strategic/Merc Contract.cpp +++ b/Strategic/Merc Contract.cpp @@ -98,7 +98,7 @@ BOOLEAN SaveContractRenewalDataToSaveGameFile( HWFILE hFile ) { UINT32 uiNumBytesWritten; - for (int idx=0; idx < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; idx++) { + for (int idx=0; idx < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; idx++) { FileWrite( hFile, &ContractRenewalList[idx], sizeof( CONTRACT_NEWAL_LIST_NODE ), &uiNumBytesWritten ); if( uiNumBytesWritten != sizeof( CONTRACT_NEWAL_LIST_NODE ) ) { @@ -126,7 +126,7 @@ BOOLEAN LoadContractRenewalDataFromSaveGameFile( HWFILE hFile ) { UINT32 uiNumBytesRead; - for (int idx=0; idx < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; idx++) { + for (int idx=0; idx < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; idx++) { FileRead( hFile, &ContractRenewalList[idx], sizeof( CONTRACT_NEWAL_LIST_NODE ), &uiNumBytesRead ); if( uiNumBytesRead != sizeof( CONTRACT_NEWAL_LIST_NODE ) ) { diff --git a/Strategic/Queen Command.cpp b/Strategic/Queen Command.cpp index 805ba6dc..f1127e3e 100644 --- a/Strategic/Queen Command.cpp +++ b/Strategic/Queen Command.cpp @@ -550,23 +550,24 @@ BOOLEAN PrepareEnemyForSectorBattle() gfPendingEnemies = (NumEnemiesInSector( gWorldSectorX, gWorldSectorY ) > mapMaximumNumberOfEnemies ); pSector = &SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ]; - if( pSector->uiFlags & SF_USE_MAP_SETTINGS ) - { //count the number of enemy placements in a map and use those - SOLDIERINITNODE *curr = gSoldierInitHead; - ubTotalAdmins = ubTotalTroops = ubTotalElites = 0; - while( curr ) + + // OJW - 20090403 - override max ai in co-op + if (is_networked && is_server && OVERRIDE_MAX_AI == 1) + { + float totalEnemies = pSector->ubNumAdmins + pSector->ubNumTroops + pSector->ubNumElites; + ubTotalAdmins = ((float)pSector->ubNumAdmins / totalEnemies) * mapMaximumNumberOfEnemies; + ubTotalTroops = ((float)pSector->ubNumTroops / totalEnemies) * mapMaximumNumberOfEnemies; + ubTotalElites = ((float)pSector->ubNumElites / totalEnemies) * mapMaximumNumberOfEnemies; + totalEnemies = ubTotalAdmins + ubTotalTroops + ubTotalElites; + + // take care of any rounding losses + while (totalEnemies < mapMaximumNumberOfEnemies) { - if( curr->pBasicPlacement->bTeam == ENEMY_TEAM ) - { - switch( curr->pBasicPlacement->ubSoldierClass ) - { - case SOLDIER_CLASS_ADMINISTRATOR: ubTotalAdmins++; break; - case SOLDIER_CLASS_ARMY: ubTotalTroops++; break; - case SOLDIER_CLASS_ELITE: ubTotalElites++; break; - } - } - curr = curr->next; + // just fill out with standard troops + ubTotalTroops++; + totalEnemies++; } + pSector->ubNumAdmins = ubTotalAdmins; pSector->ubNumTroops = ubTotalTroops; pSector->ubNumElites = ubTotalElites; @@ -576,9 +577,37 @@ BOOLEAN PrepareEnemyForSectorBattle() } else { - ubTotalAdmins = pSector->ubNumAdmins - pSector->ubAdminsInBattle; - ubTotalTroops = pSector->ubNumTroops - pSector->ubTroopsInBattle; - ubTotalElites = pSector->ubNumElites - pSector->ubElitesInBattle; + + if( pSector->uiFlags & SF_USE_MAP_SETTINGS ) + { //count the number of enemy placements in a map and use those + SOLDIERINITNODE *curr = gSoldierInitHead; + ubTotalAdmins = ubTotalTroops = ubTotalElites = 0; + while( curr ) + { + if( curr->pBasicPlacement->bTeam == ENEMY_TEAM ) + { + switch( curr->pBasicPlacement->ubSoldierClass ) + { + case SOLDIER_CLASS_ADMINISTRATOR: ubTotalAdmins++; break; + case SOLDIER_CLASS_ARMY: ubTotalTroops++; break; + case SOLDIER_CLASS_ELITE: ubTotalElites++; break; + } + } + curr = curr->next; + } + pSector->ubNumAdmins = ubTotalAdmins; + pSector->ubNumTroops = ubTotalTroops; + pSector->ubNumElites = ubTotalElites; + pSector->ubAdminsInBattle = 0; + pSector->ubTroopsInBattle = 0; + pSector->ubElitesInBattle = 0; + } + else + { + ubTotalAdmins = pSector->ubNumAdmins - pSector->ubAdminsInBattle; + ubTotalTroops = pSector->ubNumTroops - pSector->ubTroopsInBattle; + ubTotalElites = pSector->ubNumElites - pSector->ubElitesInBattle; + } } totalCountOfStationaryEnemies = ubTotalAdmins + ubTotalTroops + ubTotalElites; diff --git a/Strategic/Quest Debug System.cpp b/Strategic/Quest Debug System.cpp index 7a65e0e4..1cc6a5d0 100644 --- a/Strategic/Quest Debug System.cpp +++ b/Strategic/Quest Debug System.cpp @@ -2817,7 +2817,7 @@ BOOLEAN CreateDestroyDisplayTextEntryBox( UINT8 ubAction, STR16 pString, TEXT_EN gfRedrawQuestDebugSystem = TRUE; //get the striong from the text field - Get16BitStringFromField( 0, zText ); + Get16BitStringFromField( 0, zText, 32 ); //if the text is not null if( zText[0] != '\0' ) diff --git a/Strategic/Quests.cpp b/Strategic/Quests.cpp index 62e75831..7f92b8c9 100644 --- a/Strategic/Quests.cpp +++ b/Strategic/Quests.cpp @@ -34,6 +34,7 @@ #endif #include "BobbyRMailOrder.h" +#include "connect.h" #define TESTQUESTS @@ -1248,10 +1249,11 @@ void InternalStartQuest( UINT8 ubQuest, INT16 sSectorX, INT16 sSectorY, BOOLEAN { gubQuest[ubQuest] = QUESTINPROGRESS; - if ( fUpdateHistory ) - { - SetHistoryFact( HISTORY_QUEST_STARTED, ubQuest, GetWorldTotalMin(), sSectorX, sSectorY ); - } + if ( fUpdateHistory ) + { + if (!is_networked) + SetHistoryFact( HISTORY_QUEST_STARTED, ubQuest, GetWorldTotalMin(), sSectorX, sSectorY ); + } } else { @@ -1330,9 +1332,12 @@ void CheckForQuests( UINT32 uiDay ) // already started if (gubQuest[QUEST_DELIVER_LETTER] == QUESTNOTSTARTED) { + StartQuest( QUEST_DELIVER_LETTER, -1, -1 ); #ifdef TESTQUESTS - ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"Started DELIVER LETTER quest"); + + if (!is_networked) + ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"Started DELIVER LETTER quest"); #endif } diff --git a/Strategic/Strategic AI.cpp b/Strategic/Strategic AI.cpp index 4cc85556..4f8346f7 100644 --- a/Strategic/Strategic AI.cpp +++ b/Strategic/Strategic AI.cpp @@ -4562,23 +4562,31 @@ void HourlyCheckStrategicAI() #ifdef JA2BETAVERSION +#include "vfs.h" +#include "PropertyContainer.h" + +static CLog& s_stratD = *CLog::Create(L"Strategic Decisions.txt", true); void LogStrategicMsg( STR8 str, ... ) { va_list argptr; CHAR8 string[512]; - +#ifndef USE_VFS FILE *fp; fp = fopen( "Strategic Decisions.txt", "a" ); if( !fp ) return; - +#endif va_start(argptr, str ); vsprintf( string, str, argptr); va_end(argptr); +#ifndef USE_VFS fprintf( fp, "%s\n", string ); +#else + s_stratD << string << CLog::endl; +#endif if( gfDisplayStrategicAILogs ) { @@ -4589,28 +4597,32 @@ void LogStrategicMsg( STR8 str, ... ) OutputDebugString( (LPCSTR)String( "%s\n", string ) ); } +#ifndef USE_VFS fclose( fp ); +#endif } void LogStrategicEvent( STR8 str, ... ) { va_list argptr; CHAR8 string[512]; - +#ifndef USE_VFS FILE *fp; fp = fopen( "Strategic Decisions.txt", "a" ); if( !fp ) return; - +#endif va_start(argptr, str ); vsprintf( string, str, argptr); va_end(argptr); - +#ifndef USE_VFS fprintf( fp, "\n%S:\n", WORLDTIMESTR ); fprintf( fp, "%s\n", string ); - +#else + s_stratD.Endl() << WORLDTIMESTR << ":" << CLog::endl << string << CLog::endl; +#endif if( gfDisplayStrategicAILogs ) { ScreenMsg( FONT_LTKHAKI, MSG_DIALOG, L"%S", string ); @@ -4619,12 +4631,14 @@ void LogStrategicEvent( STR8 str, ... ) { OutputDebugString( (LPCSTR)String( "%s\n", string ) ); } - +#ifndef USE_VFS fclose( fp ); +#endif } void ClearStrategicLog() { +#ifndef USE_VFS FILE *fp; fp = fopen( "Strategic Decisions.txt", "w" ); if( !fp ) @@ -4633,6 +4647,9 @@ void ClearStrategicLog() fprintf( fp, "STRATEGIC LOG\n" ); fclose( fp ); +#else + s_stratD.Flush(); +#endif } #endif diff --git a/Strategic/Strategic Merc Handler.cpp b/Strategic/Strategic Merc Handler.cpp index 82fedefd..32d3f503 100644 --- a/Strategic/Strategic Merc Handler.cpp +++ b/Strategic/Strategic Merc Handler.cpp @@ -1017,7 +1017,7 @@ void UpdateBuddyAndHatedCounters( void ) if (OKToCheckOpinion(ubOtherProfileID)) { pProfile->bMercOpinion[ubOtherProfileID] += (HATED_OPINION - pProfile->bMercOpinion[ubOtherProfileID]) / (pProfile->bLearnToHateCount + 1); } - } + } } else { @@ -1050,15 +1050,15 @@ void UpdateBuddyAndHatedCounters( void ) { // add to liked! if (OKToCheckOpinion(ubOtherProfileID)) { - pProfile->bBuddy[2] = pProfile->bLearnToLike; - pProfile->bMercOpinion[ubOtherProfileID] = BUDDY_OPINION; + pProfile->bBuddy[2] = pProfile->bLearnToLike; + pProfile->bMercOpinion[ubOtherProfileID] = BUDDY_OPINION; } } else if (pProfile->bLearnToLikeCount < pProfile->bLearnToLikeTime / 2) { // increase opinion of them! if (OKToCheckOpinion(ubOtherProfileID)) { - pProfile->bMercOpinion[ubOtherProfileID] += (BUDDY_OPINION - pProfile->bMercOpinion[ubOtherProfileID]) / (pProfile->bLearnToLikeCount + 1); + pProfile->bMercOpinion[ubOtherProfileID] += (BUDDY_OPINION - pProfile->bMercOpinion[ubOtherProfileID]) / (pProfile->bLearnToLikeCount + 1); } break; } diff --git a/Strategic/Strategic_2005Express.vcproj b/Strategic/Strategic_2005Express.vcproj index 07b2b897..d16031f3 100644 --- a/Strategic/Strategic_2005Express.vcproj +++ b/Strategic/Strategic_2005Express.vcproj @@ -41,7 +41,7 @@ Name="VCCLCompilerTool" AdditionalOptions="/D "_CRT_SECURE_NO_DEPRECATE"" Optimization="0" - AdditionalIncludeDirectories="..\Multiplayer" + AdditionalIncludeDirectories="..\Multiplayer;..\VFS" PreprocessorDefinitions="WIN32;_DEBUG;_LIB;" MinimalRebuild="true" BasicRuntimeChecks="3" @@ -105,7 +105,7 @@ sSectorY; bSectorZ = pTrainer->bSectorZ; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { // valid character? if( gCharactersList[ iCounter ].fValid == FALSE ) @@ -1298,7 +1298,7 @@ void BuildListOfUnpaidTrainableSectors( void ) if( guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) { - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { // valid character? if( gCharactersList[ iCounter ].fValid ) @@ -1337,11 +1337,11 @@ void BuildListOfUnpaidTrainableSectors( void ) } // now clean out repeated sectors - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1; iCounter++ ) { if( gsUnpaidStrategicSector[ iCounter ] > 0 ) { - for( iCounterB = iCounter + 1 ; iCounterB < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounterB++ ) + for( iCounterB = iCounter + 1 ; iCounterB < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounterB++ ) { if( gsUnpaidStrategicSector[ iCounterB ] == gsUnpaidStrategicSector[ iCounter ] ) { @@ -1362,7 +1362,7 @@ INT32 GetNumberOfUnpaidTrainableSectors( void ) BuildListOfUnpaidTrainableSectors(); // now count up the results - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gsUnpaidStrategicSector[ iCounter ] > 0 ) { @@ -1387,7 +1387,7 @@ void StartTrainingInAllUnpaidTrainableSectors() BuildListOfUnpaidTrainableSectors(); // pay up in each sector - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gsUnpaidStrategicSector[ iCounter ] > 0 ) { diff --git a/Strategic/mapscreen.cpp b/Strategic/mapscreen.cpp index e0465e3b..f65657b9 100644 --- a/Strategic/mapscreen.cpp +++ b/Strategic/mapscreen.cpp @@ -1851,7 +1851,7 @@ BOOLEAN SetInfoChar( UINT8 ubID ) { INT8 bCounter; - for ( bCounter = 0; bCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; bCounter++) + for ( bCounter = 0; bCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; bCounter++) { // skip invalid characters if ( gCharactersList[ bCounter ].fValid == TRUE ) @@ -3121,7 +3121,7 @@ BOOLEAN CharacterIsInTransitAndHasItemPickedUp( INT8 bCharacterNumber ) void DisplayCharacterInfo( void ) { - Assert( bSelectedInfoChar < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ); + Assert( bSelectedInfoChar < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ); Assert( gCharactersList[ bSelectedInfoChar ].fValid ); // set font buffer @@ -3313,7 +3313,7 @@ void HighLightAssignLine() UINT16 usColor; static INT32 iColorNum = STARTING_COLOR_NUM; static BOOLEAN fDelta=FALSE; - static INT32 uiOldHighlight = CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1; + static INT32 uiOldHighlight = giMAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1; INT16 usCount = 0; UINT16 usX; UINT16 usY; @@ -3323,7 +3323,7 @@ void HighLightAssignLine() // is this a valid line? if( ( giAssignHighLine == -1 ) || fShowInventoryFlag ) { - uiOldHighlight = CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1; + uiOldHighlight = giMAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1; return; } @@ -3354,7 +3354,7 @@ void HighLightAssignLine() pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - for( usCount = 0; usCount < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; usCount++ ) + for( usCount = 0; usCount < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; usCount++ ) { if( IsCharacterSelectedForAssignment( usCount ) == TRUE ) { @@ -3377,7 +3377,7 @@ void HighLightAssignLine() LineDraw( TRUE, usX, usY, usX+ASSIGN_WIDTH, usY, usColor,pDestBuf); } - if( ( ( usCount == CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1 ) ) || ( usCount != ( CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1 ) ? !( IsCharacterSelectedForAssignment( ( UINT16 )( usCount + 1 ) ) ) : 0) || ( usCount == FIRST_VEHICLE - 1 ) ) + if( ( ( usCount == giMAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1 ) ) || ( usCount != ( giMAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1 ) ? !( IsCharacterSelectedForAssignment( ( UINT16 )( usCount + 1 ) ) ) : 0) || ( usCount == FIRST_VEHICLE - 1 ) ) { LineDraw(TRUE, usX, usY+GetFontHeight(MAP_SCREEN_FONT)+2, usX+ASSIGN_WIDTH, usY+GetFontHeight(MAP_SCREEN_FONT)+2, usColor, pDestBuf); } @@ -3397,7 +3397,7 @@ void HighLightDestLine() UINT16 usColor; static INT32 iColorNum = STARTING_COLOR_NUM; static BOOLEAN fDelta=FALSE; - static INT32 uiOldHighlight = CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1; + static INT32 uiOldHighlight = giMAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1; UINT16 usCount = 0; UINT16 usX; UINT16 usY; @@ -3406,7 +3406,7 @@ void HighLightDestLine() if( ( giDestHighLine == -1 ) || fShowInventoryFlag ) { - uiOldHighlight = CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1; + uiOldHighlight = giMAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1; return; } @@ -3438,7 +3438,7 @@ void HighLightDestLine() pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - for( usCount = 0; usCount < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; usCount++ ) + for( usCount = 0; usCount < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; usCount++ ) { if( CharacterIsGettingPathPlotted( usCount ) == TRUE ) { @@ -3457,7 +3457,7 @@ void HighLightDestLine() { LineDraw( TRUE, usX+4, usY, usX+DEST_ETA_WIDTH+4, usY, usColor,pDestBuf); } - if( ( ( usCount == CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1 ) ) || ( usCount != ( CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1 ) ? !( CharacterIsGettingPathPlotted( ( UINT16 )( usCount + 1 ) ) ) : 0) || ( usCount == FIRST_VEHICLE - 1 ) ) + if( ( ( usCount == giMAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1 ) ) || ( usCount != ( giMAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1 ) ? !( CharacterIsGettingPathPlotted( ( UINT16 )( usCount + 1 ) ) ) : 0) || ( usCount == FIRST_VEHICLE - 1 ) ) { LineDraw(TRUE, usX+4, usY+GetFontHeight(MAP_SCREEN_FONT)+2, usX+DEST_ETA_WIDTH+4, usY+GetFontHeight(MAP_SCREEN_FONT)+2, usColor, pDestBuf); } @@ -3483,7 +3483,7 @@ void HighLightSleepLine() UINT16 usColor; static INT32 iColorNum = STARTING_COLOR_NUM; static BOOLEAN fDelta=FALSE; - static INT32 uiOldHighlight = CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1; + static INT32 uiOldHighlight = giMAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1; UINT16 usCount = 0; UINT16 usX, usX2; UINT16 usY; @@ -3493,7 +3493,7 @@ void HighLightSleepLine() // is this a valid line? if( ( giSleepHighLine == -1 ) || fShowInventoryFlag ) { - uiOldHighlight = CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1; + uiOldHighlight = giMAXIMUM_NUMBER_OF_PLAYER_SLOTS + 1; return; } @@ -3525,7 +3525,7 @@ void HighLightSleepLine() pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); SetClippingRegionAndImageWidth( uiDestPitchBYTES, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - for( usCount = 0; usCount < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; usCount++ ) + for( usCount = 0; usCount < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; usCount++ ) { if( IsCharacterSelectedForSleep( usCount ) == TRUE ) { @@ -3546,7 +3546,7 @@ void HighLightSleepLine() { LineDraw( TRUE, usX+4, usY, usX2, usY, usColor,pDestBuf); } - if( ( ( usCount == CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1 ) ) || ( usCount != ( CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1 ) ? !( IsCharacterSelectedForSleep( ( UINT16 )( usCount + 1 ) ) ) : 0) || ( usCount == FIRST_VEHICLE - 1 ) ) + if( ( ( usCount == giMAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1 ) ) || ( usCount != ( giMAXIMUM_NUMBER_OF_PLAYER_SLOTS - 1 ) ? !( IsCharacterSelectedForSleep( ( UINT16 )( usCount + 1 ) ) ) : 0) || ( usCount == FIRST_VEHICLE - 1 ) ) { LineDraw(TRUE, usX+4, usY+GetFontHeight(MAP_SCREEN_FONT)+2, usX2, usY+GetFontHeight(MAP_SCREEN_FONT)+2, usColor, pDestBuf); } @@ -3583,7 +3583,7 @@ void AddCharacter( SOLDIERTYPE *pCharacter ) // adding a vehicle? if( pCharacter->flags.uiStatusFlags & SOLDIER_VEHICLE ) { - while( usVehicleLoop < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) + while( usVehicleLoop < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) { if ( gCharactersList[ usVehicleLoop ].fValid ) { @@ -3600,15 +3600,15 @@ void AddCharacter( SOLDIERTYPE *pCharacter ) else { // go through character list until a blank is reached - while( (gCharactersList[usCount].fValid)&&(usCount < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS) ) + while( (gCharactersList[usCount].fValid)&&(usCount < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS) ) { usCount++; } } - Assert( usCount < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ); - if (usCount >= CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) + Assert( usCount < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ); + if (usCount >= giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) { return; } @@ -3707,7 +3707,7 @@ void DisplayCharacterList() SetFontBackground(FONT_BLACK); - for (sCount = 0; sCount < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; sCount++) + for (sCount = 0; sCount < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; sCount++) { // skip invalid characters if ( gCharactersList[sCount].fValid == TRUE ) @@ -3776,6 +3776,8 @@ void DrawMPPlayerList () INT16 usX=0; INT16 usY=0; + static UINT8 pbar_id_player[4]; + if (is_networked && is_client && is_connected) { wchar_t szPlayerName[30]; @@ -3784,46 +3786,78 @@ void DrawMPPlayerList () int row=0; for(int i=0; i < 4; i++) { - //if (i != CLIENT_NUM) - //{ - if (client_names[i] != NULL) + if (client_names[i] != NULL) + { + // setup bouds for player dirty rect and progress bar + int l,t,b,r; + l = MP_PLAYER_X+1; + r = l + MP_PLAYER_W - 1; + t = MP_ROWSTART_Y+(row*Y_SIZE); + b = t + Y_SIZE - 1; + + // set up or destroy progress bar if needed + if (client_downloading[i] == 1 && pbar_id_player[i] == 0) { - if (client_ready[i]==1) - { - SetFontForeground( FONT_GREEN ); - } - else - { - SetFontForeground( FONT_WHITE ); - } - - if (strcmp(client_names[i],"")!=0) - { - // valid player - memset(szPlayerName,0,30*sizeof(wchar_t)); - mbstowcs( szPlayerName,client_names[i],30); - FindFontCenterCoordinates((short)MP_PLAYER_X + 1, (short)(MP_ROWSTART_Y+(row*Y_SIZE)), (short)MP_PLAYER_W, (short)Y_SIZE, szPlayerName, (long)MAP_SCREEN_FONT, &usX, &usY); - DrawString( (STR16)szPlayerName , usX,usY, MAP_SCREEN_FONT); - - // check for gametype here - //wcscpy(szTeam,L"N/A"); - if (PLAYER_BSIDE==MP_TYPE_DEATHMATCH) - wcscpy(szTeam,L"N/A"); - else - wcscpy(szTeam,gszMPTeamNames[client_teams[i]]); - FindFontCenterCoordinates((short)MP_TEAM_X + 1, (short)(MP_ROWSTART_Y+(row*Y_SIZE)), (short)MP_TEAM_W, (short)Y_SIZE, szTeam, (long)MAP_SCREEN_FONT, &usX, &usY); - DrawString( szTeam,usX,(short)usY, MAP_SCREEN_FONT); - - wcscpy(szCompass,(RANDOM_SPAWN==1 ? L"?" : gszMPEdgesText[client_edges[i]])); - FindFontCenterCoordinates((short)MP_COMPASS_X + 1, (short)(MP_ROWSTART_Y+(row*Y_SIZE)), (short)MP_COMPASS_W, (short)Y_SIZE, szCompass, (long)MAP_SCREEN_FONT, &usX, &usY); - DrawString(szCompass , usX,(short)usY, MAP_SCREEN_FONT); - - row++; - } - + fTeamPanelDirty = true; + + CreateProgressBarNoBorder(i,l,t,r,b); + SetProgressBarColor(i,112,154,209); + SetProgressBarRenderBuffer(i,guiSAVEBUFFER); + pbar_id_player[i] = 1; + } + else if (client_downloading[i] == 0 && pbar_id_player[i] == 1) + { + fTeamPanelDirty = true; + RemoveProgressBar(i); + pbar_id_player[i] = 0; + } + + // render the progress bar if needed + if (client_downloading[i] == 1 && pbar_id_player[i] == 1) + { + RenderProgressBar(i,client_progress[i]); } - //} + if (client_ready[i]==1) + { + SetFontForeground( FONT_GREEN ); + } + else + { + SetFontForeground( FONT_WHITE ); + } + + if (strcmp(client_names[i],"")!=0) + { + // valid player + memset(szPlayerName,0,30*sizeof(wchar_t)); + mbstowcs( szPlayerName,client_names[i],30); + FindFontCenterCoordinates((short)MP_PLAYER_X + 1, (short)(MP_ROWSTART_Y+(row*Y_SIZE)), (short)MP_PLAYER_W, (short)Y_SIZE, szPlayerName, (long)MAP_SCREEN_FONT, &usX, &usY); + // do it manually here so as DrawString overrides the dirty rects + // we want the dirty rect to include progress bar as well as player text + SetFont(MAP_SCREEN_FONT); + mprintf(usX,usY,(STR16)szPlayerName); + int iBack = RegisterBackgroundRect(BGND_FLAG_SINGLE, NULL, l, t, r, b); + SetBackgroundRectFilled( iBack ); + //DrawString( (STR16)szPlayerName , usX,usY, MAP_SCREEN_FONT); + + // check for gametype here + //wcscpy(szTeam,L"N/A"); + if (PLAYER_BSIDE==MP_TYPE_DEATHMATCH) + wcscpy(szTeam,L"N/A"); + else + wcscpy(szTeam,gszMPTeamNames[client_teams[i]]); + FindFontCenterCoordinates((short)MP_TEAM_X + 1, (short)(MP_ROWSTART_Y+(row*Y_SIZE)), (short)MP_TEAM_W, (short)Y_SIZE, szTeam, (long)MAP_SCREEN_FONT, &usX, &usY); + DrawString( szTeam,usX,(short)usY, MAP_SCREEN_FONT); + + wcscpy(szCompass,(RANDOM_SPAWN==1 ? L"?" : gszMPEdgesText[client_edges[i]])); + FindFontCenterCoordinates((short)MP_COMPASS_X + 1, (short)(MP_ROWSTART_Y+(row*Y_SIZE)), (short)MP_COMPASS_W, (short)Y_SIZE, szCompass, (long)MAP_SCREEN_FONT, &usX, &usY); + DrawString(szCompass , usX,(short)usY, MAP_SCREEN_FONT); + + row++; + } + + } } // Draw the Server Info @@ -3875,15 +3909,28 @@ void DrawMPPlayerList () swprintf(szSquadSize,L"%i",MAX_MERCS); DrawString( szSquadSize , MP_GAMEINFO_X + StringPixLength(gszMPMapscreenText[2],MAP_SCREEN_FONT),MP_ROWSTART_Y+(3*Y_SIZE), MAP_SCREEN_FONT); + row = 4; if (RANDOM_MERCS) { // Random Mercs SetFontForeground( FONT_YELLOW ); - DrawString( gszMPMapscreenText[5] , MP_GAMEINFO_X ,MP_ROWSTART_Y+(4*Y_SIZE), MAP_SCREEN_FONT); + DrawString( gszMPMapscreenText[5] , MP_GAMEINFO_X ,MP_ROWSTART_Y+(row*Y_SIZE), MAP_SCREEN_FONT); SetFontForeground( FONT_WHITE ); - DrawString( gszMPMapscreenText[6] , MP_GAMEINFO_X + StringPixLength(gszMPMapscreenText[5],MAP_SCREEN_FONT),MP_ROWSTART_Y+(4*Y_SIZE), MAP_SCREEN_FONT); + DrawString( gszMPMapscreenText[6] , MP_GAMEINFO_X + StringPixLength(gszMPMapscreenText[5],MAP_SCREEN_FONT),MP_ROWSTART_Y+(row*Y_SIZE), MAP_SCREEN_FONT); + row++; } + // Server Version + SetFontForeground( FONT_YELLOW ); + DrawString( gszMPMapscreenText[8] , MP_GAMEINFO_X ,MP_ROWSTART_Y+(row*Y_SIZE), MAP_SCREEN_FONT); + row++; + + wchar_t szServerVer[30]; + memset(szServerVer,0,30*sizeof(wchar_t)); + mbstowcs( szServerVer,MPVERSION,30); + SetFontForeground( FONT_WHITE ); + DrawString( szServerVer , MP_GAMEINFO_X ,MP_ROWSTART_Y+(row*Y_SIZE), MAP_SCREEN_FONT); + } } void DestroyMPCompassBox( void ) @@ -5257,9 +5304,9 @@ UINT32 MapScreenHandle(void) { fShowInventoryFlag = FALSE; // CHRISL: Added call to re-render the bottom panel -// RenderMapScreenInterfaceBottom ( TRUE ); + RenderMapScreenInterfaceBottom ( TRUE ); // set help text for item glow region -//XXXj? SetRegionFastHelpText( &gCharInfoHandRegion, pMiscMapScreenMouseRegionHelpText[ 0 ] ); + SetRegionFastHelpText( &gCharInfoHandRegion, pMiscMapScreenMouseRegionHelpText[ 0 ] ); } @@ -6536,20 +6583,25 @@ void GetMapKeyboardInput( UINT32 *puiNewEvent ) case BACKSPACE: StopAnyCurrentlyTalkingSpeech( ); break; - - // multiplayer: Roman: Should be changed, because the keys are already bount to another feature + case F1: + // WANNE: No more needed + /* if (is_networked) { mp_help(); break; } + */ case F2: + // WANNE: No more needed + /* if (is_networked) { mp_help2(); break; } + */ case F3: case F4: case F5: @@ -9319,7 +9371,7 @@ void CreateMouseRegionsForTeamList( void ) // the info region...is the background for the list itself // OJW - MP - int max_rows = CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; + int max_rows = giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; if (is_networked) max_rows = 7; // check this value is correct / unhardcode it @@ -9415,7 +9467,7 @@ void DestroyMouseRegionsForTeamList( void ) // will destroy mouse regions overlaying the team list area INT32 sCounter = 0; - for( sCounter = 0; sCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; sCounter++ ) + for( sCounter = 0; sCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; sCounter++ ) { MSYS_RemoveRegion( &gTeamListNameRegion[ sCounter ]); MSYS_RemoveRegion( &gTeamListAssignmentRegion[ sCounter ]); @@ -9463,7 +9515,7 @@ void MPReadyButtonCallback( GUI_BUTTON *btn, INT32 reason ) else if (allowlaptop) { // warn the user that cannot change once buying has commenced - ScreenMsg( FONT_LTBLUE, MSG_CHAT, gszMPMapscreenText[3]); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, gszMPMapscreenText[3]); } } else @@ -9491,11 +9543,11 @@ void MPReadyButtonCallback( GUI_BUTTON *btn, INT32 reason ) else if (allowlaptop) { // warn the user that cannot change once buying has commenced - ScreenMsg( FONT_LTBLUE, MSG_CHAT, gszMPMapscreenText[4]); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, gszMPMapscreenText[4]); } else if (RANDOM_SPAWN) { - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"Cannot change edge, the game is set to random spawn"); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, L"Cannot change edge, the game is set to random spawn"); } } else @@ -9528,7 +9580,7 @@ void MPCompassChangeCallback( MOUSE_REGION * pReason, INT32 iReason) else if (allowlaptop) { // warn the user that cannot change once buying has commenced - ScreenMsg( FONT_LTBLUE, MSG_CHAT, gszMPMapscreenText[3]); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, gszMPMapscreenText[3]); } } else @@ -9557,11 +9609,11 @@ void MPTeamChangeCallback( MOUSE_REGION * pReason, INT32 iReason) else if (allowlaptop) { // warn the user that cannot change once buying has commenced - ScreenMsg( FONT_LTBLUE, MSG_CHAT, gszMPMapscreenText[4]); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, gszMPMapscreenText[4]); } else if (RANDOM_SPAWN) { - ScreenMsg( FONT_LTBLUE, MSG_CHAT, L"Cannot change edge, the game is set to random spawn"); + ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, L"Cannot change edge, the game is set to random spawn"); } } else @@ -9939,7 +9991,7 @@ void TeamListAssignmentRegionBtnCallBack(MOUSE_REGION *pRegion, INT32 iReason ) // Select all characters in squad INT16 iCounter; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[ iCounter ].fValid == TRUE ) { @@ -11054,20 +11106,22 @@ void ReBuildCharactersList( void ) // add in characters - for ( sCount = 0; sCount < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; sCount++) + for ( sCount = 0; sCount < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; sCount++) { // clear this slot gCharactersList[ sCount ].fValid = FALSE; gCharactersList[ sCount ].usSolID = 0; } - for ( sCount = 0; sCount < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; sCount++) + // fills array with pressence of player controlled characters + for ( INT32 cnt=gTacticalStatus.Team[ OUR_TEAM ].bFirstID; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++) { - // add character into the cleared slot - AddCharacter( &Menptr[ gTacticalStatus.Team[ OUR_TEAM ].bFirstID + sCount ] ); + if(Menptr[ cnt ].bActive == 1) + { + AddCharacter( &Menptr[ cnt ] ); + } } - // sort them according to current sorting method SortListOfMercsInTeamPanel( FALSE ); @@ -11156,7 +11210,7 @@ void EnableDisableTeamListRegionsAndHelpText( void ) INT8 bCharNum; - for( bCharNum = 0; bCharNum < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; bCharNum++ ) + for( bCharNum = 0; bCharNum < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; bCharNum++ ) { if( gCharactersList[ bCharNum ].fValid == FALSE ) { @@ -11351,7 +11405,7 @@ BOOLEAN ContinueDialogue(SOLDIERTYPE *pSoldier, BOOLEAN fDone ) } // otherwise, find this character - for( bCounter = 0; bCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; bCounter++ ) + for( bCounter = 0; bCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; bCounter++ ) { if( gCharactersList[bCounter].fValid == TRUE ) { @@ -11378,7 +11432,7 @@ void HandleSpontanousTalking( ) if( DialogueActive() == FALSE ) { - if( ( bSelectedInfoChar != -1 ) && ( bSelectedInfoChar < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ) + if( ( bSelectedInfoChar != -1 ) && ( bSelectedInfoChar < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ) { ContinueDialogue( ( &Menptr[gCharactersList[bSelectedInfoChar].usSolID] ), TRUE ); } @@ -11514,7 +11568,7 @@ void RebuildWayPointsForAllSelectedCharsGroups( void ) memset( fGroupIDRebuilt, FALSE, sizeof( fGroupIDRebuilt ) ); - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( ( fSelectedListOfMercsForMapScreen[ iCounter ] == TRUE ) ) { @@ -11946,7 +12000,7 @@ INT8 GetLastValidCharacterInTeamPanelList( void ) INT8 iCounter = 0, iValue = 0; // run through the list and find the last valid guy in the list - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[ iCounter ].fValid == TRUE ) { @@ -12254,7 +12308,7 @@ void UpdateBadAssignments( void ) { UINT32 iCounter; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[ iCounter ].fValid == TRUE ) { @@ -12312,7 +12366,7 @@ BOOLEAN AnyMercsLeavingRealSoon() UINT32 uiTimeInMin = GetWorldTotalMin(); BOOLEAN fFoundOne = FALSE; - for( uiCounter = 0; uiCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; uiCounter++ ) + for( uiCounter = 0; uiCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; uiCounter++ ) { if( gCharactersList[ uiCounter ].fValid == TRUE ) { @@ -12892,7 +12946,7 @@ void TellPlayerWhyHeCantCompressTime( void ) // WANNE - MP: It is forbidden to compress time in multiplayer else if (is_networked) { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, L"You cannot compress time in multiplayer game. Press '3' to start the battle."); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[65]); } else if ( !AnyUsableRealMercenariesOnTeam() ) { @@ -13032,15 +13086,17 @@ void AddTeamPanelSortButtonsForMapScreen( void ) void AddMPButtonsForMapScreen( void ) { INT32 iCounter = 0; - //SGPFILENAME filename; + SGPFILENAME filename; INT32 iImageIndex[ MAX_MP_BUTTONS+1 ] = { 0, 1, 3, 5 , 7 }; // sleep image is out or order (last) INT32 iPressedIndex[ MAX_MP_BUTTONS+1 ] = { 0 , 2 , 4 , 6 , 8 }; + GetMLGFilename( filename, MLG_MP_GOLDPIECEBUTTONS ); + // add start game button and hide it... for( iCounter = 0; iCounter < MAX_MP_BUTTONS+1; iCounter++ ) { - giMapMPButtonImage[ iCounter ] = LoadButtonImage( "INTERFACE\\MPGOLDPIECEBUTTONS.STI", -1, iImageIndex[ iCounter ] , -1, iPressedIndex[ iCounter ] , -1 ); + giMapMPButtonImage[ iCounter ] = LoadButtonImage( filename, -1, iImageIndex[ iCounter ] , -1, iPressedIndex[ iCounter ] , -1 ); if (iCounter < MAX_MP_BUTTONS) { @@ -13084,7 +13140,7 @@ void SortListOfMercsInTeamPanel( BOOLEAN fRetainSelectedMercs, BOOLEAN fReverse } - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { // set current entry to null pSelectedSoldier[ iCounter ] = NULL; @@ -13321,11 +13377,11 @@ void SortListOfMercsInTeamPanel( BOOLEAN fRetainSelectedMercs, BOOLEAN fReverse // now select all the soldiers that were selected before - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( pSelectedSoldier[ iCounter ] ) { - for( iCounterA = 0; iCounterA < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounterA++ ) + for( iCounterA = 0; iCounterA < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounterA++ ) { // is this entry even valid if( gCharactersList[ iCounterA ].fValid == FALSE ) @@ -13463,7 +13519,7 @@ void HandleAssignmentsDoneAndAwaitingFurtherOrders( void ) // update timer so that we only run check so often giFlashAssignBaseTime = iCurrentTime; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[ iCounter ].fValid == FALSE ) { @@ -13501,7 +13557,7 @@ void DisplayIconsForMercsAsleep( void ) GetVideoObject(&hHandle, guiSleepIcon ); - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[ iCounter ].fValid == TRUE ) { @@ -13618,7 +13674,7 @@ BOOLEAN MapCharacterHasAccessibleInventory( INT8 bCharNumber ) Assert( bCharNumber >= 0 ); - Assert( bCharNumber < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ); + Assert( bCharNumber < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ); // invalid character slot selected? if( gCharactersList[ bCharNumber ].fValid == FALSE ) @@ -13957,7 +14013,7 @@ BOOLEAN HandleCtrlOrShiftInTeamPanel( INT8 bCharNumber, BOOLEAN fFromRightClickA INT16 iCounter; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[ iCounter ].fValid == TRUE ) { @@ -14012,7 +14068,7 @@ BOOLEAN HandleCtrlOrShiftInTeamPanel( INT8 bCharNumber, BOOLEAN fFromRightClickA INT16 iCounter; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( gCharactersList[ iCounter ].fValid == TRUE ) { @@ -14118,7 +14174,7 @@ INT32 GetContractExpiryTime( SOLDIERTYPE *pSoldier ) void ChangeSelectedInfoChar( INT8 bCharNumber, BOOLEAN fResetSelectedList ) { - Assert( ( bCharNumber >= -1 ) && ( bCharNumber < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ); + Assert( ( bCharNumber >= -1 ) && ( bCharNumber < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ) ); if( ( bCharNumber != -1 ) && ( gCharactersList[ bCharNumber ].fValid == FALSE ) ) return; @@ -14194,7 +14250,7 @@ void CopyPathToAllSelectedCharacters( PathStPtr pPath ) // run through list and copy paths for each selected character - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( fSelectedListOfMercsForMapScreen[ iCounter ] == TRUE ) { @@ -14232,7 +14288,7 @@ void CancelPathsOfAllSelectedCharacters() // cancel destination for the clicked and ALL other valid & selected characters with a route set - for( bCounter = 0; bCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; bCounter++ ) + for( bCounter = 0; bCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; bCounter++ ) { // if we've clicked on a selected valid character if( ( gCharactersList[ bCounter ].fValid == TRUE ) && IsEntryInSelectedListSet( bCounter ) ) @@ -14315,7 +14371,7 @@ INT16 CalcLocationValueForChar( INT32 iCounter ) SOLDIERTYPE *pSoldier = NULL; INT16 sLocValue = 0; - Assert( iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ); + Assert( iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS ); if( gCharactersList[ iCounter ].fValid == FALSE ) return( sLocValue ); @@ -14620,7 +14676,7 @@ void RandomAwakeSelectedMercConfirmsStrategicMove( void ) UINT8 ubChosenMerc; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( ( fSelectedListOfMercsForMapScreen[ iCounter ] == TRUE ) ) { @@ -14789,10 +14845,10 @@ BOOLEAN CanMoveBullseyeAndClickedOnIt( INT16 sMapX, INT16 sMapY ) // haydent else if(is_server) { - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPServerMessage[9] ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPServerMessage[9] ); } else if(is_client) - ScreenMsg( FONT_LTGREEN, MSG_CHAT, MPClientMessage[38] ); + ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[38] ); } else { @@ -14849,7 +14905,7 @@ void WakeUpAnySleepingSelectedMercsOnFootOrDriving( void ) BOOLEAN fSuccess = FALSE; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( ( fSelectedListOfMercsForMapScreen[ iCounter ] == TRUE ) ) { @@ -15086,7 +15142,7 @@ void InitPreviousPaths( void ) INT32 iCounter = 0; // init character previous paths - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { gpCharacterPreviousMercPath[ iCounter ] = NULL; } @@ -15101,7 +15157,7 @@ void RememberPreviousPathForAllSelectedChars( void ) INT32 iCounter = 0; SOLDIERTYPE *pSoldier = NULL; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( fSelectedListOfMercsForMapScreen[ iCounter ] == TRUE ) { @@ -15168,7 +15224,7 @@ void RestorePreviousPaths( void ) } else // character(s) plotting { - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { // if selected if( fSelectedListOfMercsForMapScreen[ iCounter ] == TRUE ) @@ -15241,7 +15297,7 @@ void ClearPreviousPaths( void ) { INT32 iCounter = 0; - for( iCounter = 0; iCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) + for( iCounter = 0; iCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; iCounter++ ) { if( fSelectedListOfMercsForMapScreen[ iCounter ] == TRUE ) { @@ -15268,7 +15324,7 @@ void SelectAllCharactersInSquad( INT8 bSquadNumber ) ChangeSelectedInfoChar( -1, TRUE ); // now select all the soldiers that are in this squad - for( bCounter = 0; bCounter < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; bCounter++ ) + for( bCounter = 0; bCounter < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; bCounter++ ) { // is this entry is valid if( gCharactersList[ bCounter ].fValid == TRUE ) @@ -15377,8 +15433,7 @@ void RequestToggleMercInventoryPanel( void ) else { // Headrock. New line forces InvBottom rerender when the inventory is opened and closed. - //RenderMapScreenInterfaceBottom ( TRUE ); - fMapScreenBottomDirty = TRUE; + RenderMapScreenInterfaceBottom ( TRUE ); SetRegionFastHelpText( &gCharInfoHandRegion, pMiscMapScreenMouseRegionHelpText[ 0 ] ); } } diff --git a/Strategic/strategicmap.cpp b/Strategic/strategicmap.cpp index faed22da..8a460f97 100644 --- a/Strategic/strategicmap.cpp +++ b/Strategic/strategicmap.cpp @@ -109,6 +109,9 @@ #include "connect.h" //hayden added alot ""'s to get around client spawing random/different placed AI #include "SaveLoadGame.h" +#include "vfs.h" +#include "PropertyContainer.h" + //forward declarations of common classes to eliminate includes class OBJECTTYPE; class SOLDIERTYPE; @@ -1377,6 +1380,7 @@ void EndLoadScreen( ) 'A' + gWorldSectorY - 1, gWorldSectorX, gbWorldSectorZ, uiSeconds, uiHundreths ); } ScreenMsg( FONT_YELLOW, MSG_TESTVERSION, str ); +#ifndef USE_VFS if( fStartNewFile ) { //start new file fp = fopen( "TimeResults.txt", "w" ); @@ -1447,6 +1451,65 @@ void EndLoadScreen( ) fclose( fp ); } +#else + CLog timeResults(L"TimeResults.txt", true); + ScreenMsg( FONT_YELLOW, MSG_TESTVERSION, L"See JA2\\Data\\TimeResults.txt for more detailed timings." ); + + //Record all of the timings. + timeResults << str << CLog::endl; + timeResults << "EnterSector() supersets LoadWorld(). This includes other external sections." << CLog::endl; + //FileRead() + timeResults << CLog::endl << CLog::endl << "VARIOUS FUNCTION TIMINGS (exclusive of actual function timings in second heading)" << CLog::endl; + uiSeconds = uiTotalFileReadTime / 1000; + uiHundreths = (uiTotalFileReadTime / 10) % 100; + //fprintf( fp, "FileRead: %d.%02d (called %d times)\n", uiSeconds, uiHundreths, uiTotalFileReadCalls ); + timeResults << "FileRead: " <bActive /*&& pSoldier->bInSector*/ && pSoldier->stats.bLife && pSoldier->ubProfile == NO_PROFILE ) + if( pSoldier && pSoldier->bActive /*&& pSoldier->bInSector*/ && pSoldier->stats.bLife && pSoldier->ubProfile == NO_PROFILE ) { //soldier is valid, so find the matching soldier init list entry for modification. curr = gSoldierInitHead; while( curr && curr->pSoldier != pSoldier ) diff --git a/Tactical/Interface.cpp b/Tactical/Interface.cpp index 94defc9e..670109e2 100644 --- a/Tactical/Interface.cpp +++ b/Tactical/Interface.cpp @@ -2966,7 +2966,7 @@ BOOLEAN AddTopMessage( UINT8 ubType, STR16 pzString ) gTacticalStatus.fInTopMessage = TRUE; // Copy string - wcscpy( gTacticalStatus.zTopMessageString, pzString ); + wcsncpy( gTacticalStatus.zTopMessageString, pzString, 19 ); CreateTopMessage( gTopMessage.uiSurface, ubType, pzString ); diff --git a/Tactical/Overhead Types.h b/Tactical/Overhead Types.h index 1e88b725..2f5fd46b 100644 --- a/Tactical/Overhead Types.h +++ b/Tactical/Overhead Types.h @@ -273,12 +273,19 @@ const int startingZ = 0; // ENUMERATION OF SOLDIER POSIITONS IN GLOBAL SOLDIER LIST // WDS - make number of mercenaries, etc. be configurable #define CODE_MAXIMUM_NUMBER_OF_PLAYER_MERCS 32 +extern UINT8 giMAXIMUM_NUMBER_OF_PLAYER_MERCS; #define CODE_MAXIMUM_NUMBER_OF_PLAYER_VEHICLES 6 +extern UINT8 giMAXIMUM_NUMBER_OF_PLAYER_VEHICLES; #define CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS CODE_MAXIMUM_NUMBER_OF_PLAYER_MERCS+CODE_MAXIMUM_NUMBER_OF_PLAYER_VEHICLES +extern UINT8 giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; #define CODE_MAXIMUM_NUMBER_OF_ENEMIES 64 +extern UINT8 giMAXIMUM_NUMBER_OF_ENEMIES; #define CODE_MAXIMUM_NUMBER_OF_CREATURES 40 +extern UINT8 giMAXIMUM_NUMBER_OF_CREATURES; #define CODE_MAXIMUM_NUMBER_OF_REBELS 64 +extern UINT8 giMAXIMUM_NUMBER_OF_REBELS; #define CODE_MAXIMUM_NUMBER_OF_CIVS 40 +extern UINT8 giMAXIMUM_NUMBER_OF_CIVS; // The following should be the largest from the above set of constants. // Note: Is there any way to compute this via the preprocessor? diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index 424d6171..4dd5a924 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -117,6 +117,16 @@ #include "test_space.h" #include "connect.h" +// OJW - 20090419 +UINT8 giMAXIMUM_NUMBER_OF_PLAYER_MERCS = CODE_MAXIMUM_NUMBER_OF_PLAYER_MERCS; +UINT8 giMAXIMUM_NUMBER_OF_PLAYER_VEHICLES = CODE_MAXIMUM_NUMBER_OF_PLAYER_VEHICLES; +UINT8 giMAXIMUM_NUMBER_OF_PLAYER_SLOTS = CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; +UINT8 giMAXIMUM_NUMBER_OF_ENEMIES = CODE_MAXIMUM_NUMBER_OF_ENEMIES; +UINT8 giMAXIMUM_NUMBER_OF_CREATURES = CODE_MAXIMUM_NUMBER_OF_CREATURES; +UINT8 giMAXIMUM_NUMBER_OF_REBELS = CODE_MAXIMUM_NUMBER_OF_REBELS; +UINT8 giMAXIMUM_NUMBER_OF_CIVS = CODE_MAXIMUM_NUMBER_OF_CIVS; + + //forward declarations of common classes to eliminate includes class OBJECTTYPE; class SOLDIERTYPE; @@ -362,16 +372,16 @@ UINT8 bDefaultTeamRanges[ MAXTEAMS_SP ][ 2 ] = COLORVAL bDefaultTeamColors[ MAXTEAMS ] = { - FROMRGB( 255, 255, 0 ), + FROMRGB( 255, 255, 0 ), // own team: yellow FROMRGB( 255, 0, 0 ), FROMRGB( 255, 0, 255 ), FROMRGB( 0, 255, 0 ), FROMRGB( 255, 255, 255 ), FROMRGB( 0, 0, 255 ), - FROMRGB( 255, 156, 49 ), //hayden //team 1 (radar colours) - FROMRGB( 49, 255, 207 ), //2 - FROMRGB( 193, 85, 255 ), //3 - FROMRGB( 0, 255, 115 ) //4 + FROMRGB( 255, 120, 0 ), //hayden //team 1 (radar colours) // orange + FROMRGB( 62, 140, 240 ), //2 // light blue + FROMRGB( 180, 50, 255 ), //3 // violett + FROMRGB( 0, 180, 20 ) //4 // green }; @@ -7075,16 +7085,19 @@ BOOLEAN CheckForLosingEndOfBattle( ) gfKillingGuysForLosingBattle = FALSE; - if ( fDoCapture ) + // WANNE - MP: Only do special scenes (capture, ...) in a single player game + if (!is_networked) { - EndCaptureSequence( ); - SetCustomizableTimerCallbackAndDelay( 3000, CaptureTimerCallback, FALSE ); + if ( fDoCapture ) + { + EndCaptureSequence( ); + SetCustomizableTimerCallbackAndDelay( 3000, CaptureTimerCallback, FALSE ); + } + else + { + SetCustomizableTimerCallbackAndDelay( 10000, DeathTimerCallback, FALSE ); + } } - else - { - SetCustomizableTimerCallbackAndDelay( 10000, DeathTimerCallback, FALSE ); - } - } return( TRUE ); } diff --git a/Tactical/Soldier Create.cpp b/Tactical/Soldier Create.cpp index 6d274d0a..79da8dbe 100644 --- a/Tactical/Soldier Create.cpp +++ b/Tactical/Soldier Create.cpp @@ -441,7 +441,7 @@ SOLDIERTYPE* TacticalCreateSoldier( SOLDIERCREATE_STRUCT *pCreateStruct, UINT8 * if(is_networked) { if(is_networked && (pCreateStruct->fOnRoof==1)) { - ScreenMsg( FONT_YELLOW, MSG_CHAT, L"skipping roof merc"); + ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"skipping roof merc"); return NULL; } @@ -462,7 +462,7 @@ SOLDIERTYPE* TacticalCreateSoldier( SOLDIERCREATE_STRUCT *pCreateStruct, UINT8 * if(is_networked && (pCreateStruct->bBodyType==23 || pCreateStruct->bBodyType==24)) { - ScreenMsg( FONT_YELLOW, MSG_CHAT, L"skipping tank"); + ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"skipping tank"); return NULL; } } diff --git a/Tactical/Soldier Init List.cpp b/Tactical/Soldier Init List.cpp index 8ffcd478..796601a3 100644 --- a/Tactical/Soldier Init List.cpp +++ b/Tactical/Soldier Init List.cpp @@ -753,7 +753,7 @@ BOOLEAN AddPlacementToWorld( SOLDIERINITNODE *curr, GROUP *pGroup = NULL ) } DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("AddPlacementToWorld: return false")); - if(is_server) ScreenMsg( FONT_YELLOW, MSG_CHAT, L"report this MP error (AddPlacementToWorld-FAIL!)"); + if(is_server) ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"report this MP error (AddPlacementToWorld-FAIL!)"); return FALSE; } diff --git a/Tactical/Squads.cpp b/Tactical/Squads.cpp index eeeb98bb..ad7d97af 100644 --- a/Tactical/Squads.cpp +++ b/Tactical/Squads.cpp @@ -485,8 +485,9 @@ BOOLEAN RemoveCharacterFromSquads( SOLDIERTYPE *pCharacter ) AddDeadCharacterToSquadDeadGuys( pCharacter, iCounterA ); } - //if we are not loading a saved game - if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) && guiCurrentScreen == GAME_SCREEN ) + //if we are not loading a saved game + // OJW - 20090427 - fix bug leaving gamescree/tactical for MAIN_MENU + if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) && guiCurrentScreen == GAME_SCREEN && guiPendingScreen != MAINMENU_SCREEN ) { UpdateCurrentlySelectedMerc( pCharacter, ( INT8 )iCounterA ); } diff --git a/Tactical/Tactical Save.cpp b/Tactical/Tactical Save.cpp index e5b44c45..d18fcd1b 100644 --- a/Tactical/Tactical Save.cpp +++ b/Tactical/Tactical Save.cpp @@ -48,6 +48,8 @@ #include "Map Screen Interface Map Inventory.h" #endif +#include "VFS/vfs.h" + BOOLEAN gfWasInMeanwhile = FALSE; @@ -1546,6 +1548,7 @@ BOOLEAN RetrieveTempFileFromSavedGame( HWFILE hFile, UINT32 uiType, INT16 sMapX, //Deletes the Temp map Directory BOOLEAN InitTacticalSave( BOOLEAN fCreateTempDir ) { +#ifndef USE_VFS UINT32 uiRetVal; //If the Map Temp directory exists, removes the temp files @@ -1569,13 +1572,14 @@ BOOLEAN InitTacticalSave( BOOLEAN fCreateTempDir ) AssertMsg( 0, "Error creating the Temp Directory."); } } - +#else + EraseDirectory( MAPS_DIR ); +#endif if( fCreateTempDir ) { //Create the initial temp file for the Npc Quote Info InitTempNpcQuoteInfoForNPCFromTempFile(); } - return( TRUE ); } diff --git a/Tactical/TeamTurns.cpp b/Tactical/TeamTurns.cpp index d210c80d..a00da440 100644 --- a/Tactical/TeamTurns.cpp +++ b/Tactical/TeamTurns.cpp @@ -932,16 +932,18 @@ void StartInterrupt( void ) } // otherwise it's the AI interrupting another AI team - gTacticalStatus.ubCurrentTeam = pSoldier->bTeam; - - //#ifdef JA2BETAVERSION + if (pSoldier != NULL) + gTacticalStatus.ubCurrentTeam = pSoldier->bTeam; if (is_networked) - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Interrupt ( could be hidden )" ); - - //#endif - //send_interrupt( pSoldier );//hayden - StartNPCAI( pSoldier ); + { + #ifdef JA2BETAVERSION + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Interrupt ( could be hidden )" ); + #endif + } + + if (pSoldier != NULL) + StartNPCAI( pSoldier ); } } @@ -1776,11 +1778,14 @@ INT8 CalcInterruptDuelPts( SOLDIERTYPE * pSoldier, UINT8 ubOpponentID, BOOLEAN f #ifdef DEBUG_INTERRUPTS DebugMsg( TOPIC_JA2INTERRUPT, DBG_LEVEL_3, String("Calculating int pts for %d vs %d, number is %d", pSoldier->ubID, ubOpponentID, iPoints ) ); #endif -if(is_networked) -{ - SOLDIERTYPE *pOpp = &Menptr[ubOpponentID]; - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_CHAT, L"Interrupt: '%s' vs '%s' = %d points.",pSoldier->name,pOpp->name, iPoints ); -} + if(is_networked) + { + SOLDIERTYPE *pOpp = &Menptr[ubOpponentID]; + + #ifdef JA2BETAVERSION + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_MPSYSTEM, L"Interrupt: '%s' vs '%s' = %d points.",pSoldier->name,pOpp->name, iPoints ); + #endif + } DebugMsg (TOPIC_JA2INTERRUPT,DBG_LEVEL_3,"CalcInterruptDuelPts done"); return( (INT8)iPoints ); } diff --git a/Tactical/Turn Based Input.cpp b/Tactical/Turn Based Input.cpp index 25aceec6..f55dd615 100644 --- a/Tactical/Turn Based Input.cpp +++ b/Tactical/Turn Based Input.cpp @@ -2366,27 +2366,31 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) } } else if ( fCtrl ) // The_Bob - real time sneaking, 01-06-09 - { // ctrl-x: enter turn based while sneaking - check if RT sneak is on, iw we're not already in combat and if we actually see any enemies - if (gGameExternalOptions.fAllowRealTimeSneak) + { + if (!is_networked) { - BOOLEAN fSneakingInRealTime = true; + // ctrl-x: enter turn based while sneaking - check if RT sneak is on, iw we're not already in combat and if we actually see any enemies + if (gGameExternalOptions.fAllowRealTimeSneak) + { + BOOLEAN fSneakingInRealTime = true; - if( gTacticalStatus.uiFlags & INCOMBAT ) - { // Don't allow this in combat - if (!gGameExternalOptions.fQuietRealTimeSneak) - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_RTM_IN_COMBAT_ALREADY]); - fSneakingInRealTime = false; - } + if( gTacticalStatus.uiFlags & INCOMBAT ) + { // Don't allow this in combat + if (!gGameExternalOptions.fQuietRealTimeSneak) + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_RTM_IN_COMBAT_ALREADY]); + fSneakingInRealTime = false; + } - if( WeSeeNoOne() ) - { // Don't allow this if no enemies are seen - we have the forced turn mode for that - if (!gGameExternalOptions.fQuietRealTimeSneak) - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_RTM_NO_ENEMIES]); - fSneakingInRealTime = false; + if( WeSeeNoOne() ) + { // Don't allow this if no enemies are seen - we have the forced turn mode for that + if (!gGameExternalOptions.fQuietRealTimeSneak) + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_RTM_NO_ENEMIES]); + fSneakingInRealTime = false; + } + + if (fSneakingInRealTime) + EnterCombatMode(OUR_TEAM); } - - if (fSneakingInRealTime) - EnterCombatMode(OUR_TEAM); } } break; @@ -2394,25 +2398,25 @@ void GetKeyboardInput( UINT32 *puiNewEvent ) case 'X': // shift-ctrl-x: toggle real time sneaking if ( fCtrl ) { - if (gGameExternalOptions.fAllowRealTimeSneak) + if (!is_networked) { - gGameExternalOptions.fAllowRealTimeSneak = false; - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_RTM_SNEAKING_OFF]); - - if( !WeSeeNoOne() ) // if we're sneaking up on someone, enter turn-based - EnterCombatMode(OUR_TEAM); + if (gGameExternalOptions.fAllowRealTimeSneak) + { + gGameExternalOptions.fAllowRealTimeSneak = false; + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_RTM_SNEAKING_OFF]); + + if( !WeSeeNoOne() ) // if we're sneaking up on someone, enter turn-based + EnterCombatMode(OUR_TEAM); + } + else + { + gGameExternalOptions.fAllowRealTimeSneak = true; + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_RTM_SNEAKING_ON]); + } } - else - { - gGameExternalOptions.fAllowRealTimeSneak = true; - ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_RTM_SNEAKING_ON]); - } - } break; - - case '/': // Center to guy.... diff --git a/Tactical/Vehicles.cpp b/Tactical/Vehicles.cpp index 7e00e07b..74e5e5f2 100644 --- a/Tactical/Vehicles.cpp +++ b/Tactical/Vehicles.cpp @@ -452,7 +452,7 @@ BOOLEAN AddSoldierToVehicle( SOLDIERTYPE *pSoldier, INT32 iId ) pVehicleSoldier = GetSoldierStructureForVehicle( iId ); //CHRISL: Get number of vehicles currently in player team - for(int x = 0; x < CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS; x++) + for(int x = 0; x < giMAXIMUM_NUMBER_OF_PLAYER_SLOTS; x++) { if(MercPtrs[x]->bTeam == OUR_TEAM && (MercPtrs[x]->flags.uiStatusFlags & SOLDIER_VEHICLE) && MercPtrs[x]->bActive == TRUE) vCount ++; diff --git a/Tactical/XML_LBEPocket.cpp b/Tactical/XML_LBEPocket.cpp index f4a45cf3..4c12d9f7 100644 --- a/Tactical/XML_LBEPocket.cpp +++ b/Tactical/XML_LBEPocket.cpp @@ -402,8 +402,7 @@ BOOLEAN ReadInLBEPocketStats(STR fileName, BOOLEAN localizedVersion) XML_SetUserData(parser, &pData); - - if(!XML_Parse(parser, lpcBuffer, uiFSize, TRUE)) + if(!XML_Parse(parser, lpcBuffer, uiFSize, TRUE)) { CHAR8 errorBuf[511]; diff --git a/Tactical/XML_LoadBearingEquipment.cpp b/Tactical/XML_LoadBearingEquipment.cpp index d935c82b..38940065 100644 --- a/Tactical/XML_LoadBearingEquipment.cpp +++ b/Tactical/XML_LoadBearingEquipment.cpp @@ -232,8 +232,8 @@ BOOLEAN ReadInlbeStats(STR fileName) XML_SetUserData(parser, &pData); - - if(!XML_Parse(parser, lpcBuffer, uiFSize, TRUE)) + LoadBearingEquipment.clear(); + if(!XML_Parse(parser, lpcBuffer, uiFSize, TRUE)) { CHAR8 errorBuf[511]; diff --git a/Tactical/XML_MercStartingGear.cpp b/Tactical/XML_MercStartingGear.cpp index f7199727..e8e15b6b 100644 --- a/Tactical/XML_MercStartingGear.cpp +++ b/Tactical/XML_MercStartingGear.cpp @@ -545,8 +545,11 @@ BOOLEAN ReadInMercStartingGearStats(STR fileName) XML_SetUserData(parser, &pData); - - if(!XML_Parse(parser, lpcBuffer, uiFSize, TRUE)) + for(int i=0; ibSectorZ ) { + // WANNE - MP: Check if the desired insertion direction is valid on the map. If not, choose another entry direction! + if (is_networked) + { + MercPtrs[ i ]->ubStrategicInsertionCode = GetValidInsertionDirectionForMP(MercPtrs[ i ]->ubStrategicInsertionCode); + } + // ATE: If we are in a vehicle - remove ourselves from it! //if ( MercPtrs[ i ]->flags.uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) //{ @@ -363,9 +372,15 @@ void InitTacticalPlacementGUI() gMercPlacement[ giPlacements ].pSoldier = MercPtrs[ i ]; gMercPlacement[ giPlacements ].ubStrategicInsertionCode = MercPtrs[ i ]->ubStrategicInsertionCode; gMercPlacement[ giPlacements ].fPlaced = FALSE; + #ifdef JA2BETAVERSION CheckForValidMapEdge( &MercPtrs[ i ]->ubStrategicInsertionCode ); + #else + // WANNE: We need to have valid map edges in multiplayer! + if (is_networked) + CheckForValidMapEdge( &MercPtrs[ i ]->ubStrategicInsertionCode ); #endif + switch( MercPtrs[ i ]->ubStrategicInsertionCode ) { case INSERTION_CODE_NORTH: @@ -445,6 +460,107 @@ void InitTacticalPlacementGUI() } } +// WANNE - MP: This method checks, if the desired entry direction (N, E, S, W) on the map is valid. If not it chooses the next valid diretion +UINT8 GetValidInsertionDirectionForMP(UINT8 currentInsertionPoint) +{ + bool foundValidDirection = false; + UINT8 validInsertionDirection = currentInsertionPoint; + + // Check if current insertion direction is valid + switch (currentInsertionPoint) + { + case INSERTION_CODE_NORTH: + if (gus1stNorthEdgepointArraySize > 0 || gus2ndNorthEdgepointArraySize > 0) + { + foundValidDirection = true; + validInsertionDirection = INSERTION_CODE_NORTH; + } + break; + case INSERTION_CODE_SOUTH: + if (gus1stSouthEdgepointArraySize > 0 || gus2ndSouthEdgepointArraySize > 0) + { + foundValidDirection = true; + validInsertionDirection = INSERTION_CODE_SOUTH; + } + break; + case INSERTION_CODE_EAST: + if (gus1stEastEdgepointArraySize > 0 || gus2ndEastEdgepointArraySize > 0) + { + foundValidDirection = true; + validInsertionDirection = INSERTION_CODE_EAST; + } + break; + case INSERTION_CODE_WEST: + if (gus1stWestEdgepointArraySize > 0 || gus2ndWestEdgepointArraySize > 0) + { + foundValidDirection = true; + validInsertionDirection = INSERTION_CODE_WEST; + } + break; + } + + // Find alternate insertion direction by looping through all directions (N, S, E, W) + if (!foundValidDirection) + { + UINT8 direction = 0; + + // Find NEXT valid direction + for (int i = currentInsertionPoint; (i < currentInsertionPoint + 4); i++) + { + // First iteration, start with current insertion direction + if (i == currentInsertionPoint) + direction = i; + else + { + if (i <= 3) + direction = i; + else + { + direction = 4 - i; + } + } + + switch (direction) + { + case INSERTION_CODE_NORTH: + if (gus1stNorthEdgepointArraySize > 0 || gus2ndNorthEdgepointArraySize > 0) + { + foundValidDirection = true; + validInsertionDirection = INSERTION_CODE_NORTH; + } + break; + case INSERTION_CODE_SOUTH: + if (gus1stSouthEdgepointArraySize > 0 || gus2ndSouthEdgepointArraySize > 0) + { + foundValidDirection = true; + validInsertionDirection = INSERTION_CODE_SOUTH; + } + break; + case INSERTION_CODE_EAST: + if (gus1stEastEdgepointArraySize > 0 || gus2ndEastEdgepointArraySize > 0) + { + foundValidDirection = true; + validInsertionDirection = INSERTION_CODE_EAST; + } + break; + case INSERTION_CODE_WEST: + if (gus1stWestEdgepointArraySize > 0 || gus2ndWestEdgepointArraySize > 0) + { + foundValidDirection = true; + validInsertionDirection = INSERTION_CODE_WEST; + } + break; + } + + // Exit loop condition + if (foundValidDirection) + break; + } + } + + return validInsertionDirection; +} + void RenderTacticalPlacementGUI() { INT32 i, xp, yp, width, height; @@ -574,17 +690,33 @@ void RenderTacticalPlacementGUI() } else { + // TODO.RW: Check for insertion + if (is_networked) + { + gMercPlacement[ gbCursorMercID ].ubStrategicInsertionCode = GetValidInsertionDirectionForMP(gMercPlacement[ gbCursorMercID ].ubStrategicInsertionCode); + } + gTPClipRect.iLeft = iOffsetHorizontal; gTPClipRect.iTop = iOffsetVertical + 3; //gTPClipRect.iRight = iOffsetHorizontal + 640; gTPClipRect.iRight = iOffsetHorizontal + 634; // 635 gTPClipRect.iBottom = iOffsetVertical + 320; + + switch( gMercPlacement[ gbCursorMercID ].ubStrategicInsertionCode ) { - case INSERTION_CODE_NORTH: gTPClipRect.iTop = iOffsetVertical + 30 + 3; break; - case INSERTION_CODE_EAST: gTPClipRect.iRight = iOffsetHorizontal + 610; break; - case INSERTION_CODE_SOUTH: gTPClipRect.iBottom = iOffsetVertical + 290; break; - case INSERTION_CODE_WEST: gTPClipRect.iLeft = iOffsetHorizontal + 30; break; + case INSERTION_CODE_NORTH: + gTPClipRect.iTop = iOffsetVertical + 30 + 3; + break; + case INSERTION_CODE_EAST: + gTPClipRect.iRight = iOffsetHorizontal + 610; + break; + case INSERTION_CODE_SOUTH: + gTPClipRect.iBottom = iOffsetVertical + 290; + break; + case INSERTION_CODE_WEST: + gTPClipRect.iLeft = iOffsetHorizontal + 30; + break; } } pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); diff --git a/TileEngine/Tactical Placement GUI.h b/TileEngine/Tactical Placement GUI.h index 237ad277..cacd60bb 100644 --- a/TileEngine/Tactical Placement GUI.h +++ b/TileEngine/Tactical Placement GUI.h @@ -11,6 +11,9 @@ void RenderTacticalPlacementGUI(); void HandleTacticalPlacementClicksInOverheadMap( MOUSE_REGION *reg, INT32 reason ); +// WANNE - MP: This method checks, if the desired entry direction (N, E, S, W) on the map is valid. If not it chooses the next valid diretion +UINT8 GetValidInsertionDirectionForMP(UINT8 currentInsertionPoint); + extern BOOLEAN gfTacticalPlacementGUIActive; extern BOOLEAN gfEnterTacticalPlacementGUI; diff --git a/TileEngine/TileEngine_2005Express.vcproj b/TileEngine/TileEngine_2005Express.vcproj index cab5175b..f2d3031e 100644 --- a/TileEngine/TileEngine_2005Express.vcproj +++ b/TileEngine/TileEngine_2005Express.vcproj @@ -1,7 +1,7 @@ bTeam == gbPlayerNum ) - { - gfUIHandleSelectionAboveGuy = TRUE; - gsSelectedGuy = pSoldier->ubID; - } + if ( pSoldier->bTeam == gbPlayerNum ) + { + gfUIHandleSelectionAboveGuy = TRUE; + gsSelectedGuy = pSoldier->ubID; + } - DisplayMercNameInOverhead( pSoldier ); - } + DisplayMercNameInOverhead( pSoldier ); + } } } @@ -529,7 +529,7 @@ void HandleOverheadMap( ) { pSoldier = MercPtrs[ gusSelectedSoldier ]; - DisplayMercNameInOverhead( pSoldier ); + DisplayMercNameInOverhead( pSoldier ); } RenderButtons( ); @@ -1272,22 +1272,44 @@ void RenderOverheadOverlays() else #endif if( !gfTacticalPlacementGUIActive ) - { //normal + { + //normal if(is_networked) { if(pSoldier->bTeam!=0) { - if(pSoldier->bSide==1)Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, 1 ); - if(pSoldier->bSide==0)Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, 3 ); + if(pSoldier->bSide==1) + { + // Civ (white) + if (pSoldier->bTeam == 4) + Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, 4 ); + // Enemy (red) + else + Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, 1 ); + } + + // Other clients + if(pSoldier->bSide==0) + { + int personIndex = pSoldier->bTeam + 4; + // TODO.RW: Get the correct person index (depending on the pSoldier->bTeam) + //Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, 3 ); + Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, personIndex ); + } } - else Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, pSoldier->bTeam ); + else + // Color depends on the bTeam + Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, pSoldier->bTeam ); } + // Color depends on the bTeam else - Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, pSoldier->bTeam ); + Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, pSoldier->bTeam ); + RegisterBackgroundRect(BGND_FLAG_SINGLE, NULL, sX, sY, (INT16)(sX + 3), (INT16)(sY + 9)); } else if( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) - { //vehicle + { + //vehicle Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, 9 ); RegisterBackgroundRect(BGND_FLAG_SINGLE, NULL, (INT16)(sX-6), (INT16)(sY), (INT16)(sX + 9), (INT16)(sY + 10)); } @@ -1296,30 +1318,49 @@ void RenderOverheadOverlays() // ubPassengers++; //} else if( gpTacticalPlacementSelectedSoldier == pSoldier ) - { //tactical placement selected merc + { + //tactical placement selected merc Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, 7 ); RegisterBackgroundRect(BGND_FLAG_SINGLE, NULL, (INT16)(sX-2), (INT16)(sY-2), (INT16)(sX + 5), (INT16)(sY + 11)); } else if( gpTacticalPlacementHilightedSoldier == pSoldier && pSoldier->flags.uiStatusFlags ) - { //tactical placement hilighted merc + { + //tactical placement hilighted merc Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, 8 ); RegisterBackgroundRect(BGND_FLAG_SINGLE, NULL, (INT16)(sX-2), (INT16)(sY-2), (INT16)(sX + 5), (INT16)(sY + 11)); } else - { //normal + { + //normal if(is_networked) { if(pSoldier->bTeam!=0) { - if(pSoldier->bSide==1)continue;//dont render enemy - if(pSoldier->bSide==0)Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, 3 ); + if(pSoldier->bSide==1) + continue;//dont render enemy + + // Green + // Other clients + if(pSoldier->bSide==0) + { + int personIndex = pSoldier->bTeam + 4; + // TODO.RW: Get the correct person index (depending on the pSoldier->bTeam) + + //Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, 3 ); + Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, personIndex ); + } } - else Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, pSoldier->bTeam ); + else + // Color depends on the bTeam + Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, pSoldier->bTeam ); } else - Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, pSoldier->bTeam ); + // Color depends on the bTeam + Blt8BPPDataTo16BPPBufferTransparent((UINT16*)pDestBuf, uiDestPitchBYTES, hVObject, sX, sY, pSoldier->bTeam ); + RegisterBackgroundRect(BGND_FLAG_SINGLE, NULL, sX, sY, (INT16)(sX + 3), (INT16)(sY + 9)); } + if( ubPassengers ) { SetFont( SMALLCOMPFONT ); diff --git a/TileEngine/worlddef.cpp b/TileEngine/worlddef.cpp index c63f01f5..a3bc70e2 100644 --- a/TileEngine/worlddef.cpp +++ b/TileEngine/worlddef.cpp @@ -522,8 +522,9 @@ extern BOOLEAN gfLoadShadeTablesFromTextFile; void BuildTileShadeTables( ) { HWFILE hfile; - STRING512 DataDir; - STRING512 ShadeTableDir; + // BF + //STRING512 DataDir; + //STRING512 ShadeTableDir; UINT32 uiLoop; CHAR8 cRootFile[ 128 ]; BOOLEAN fForceRebuildForSlot = FALSE; @@ -540,18 +541,22 @@ void BuildTileShadeTables( ) uiStartTime = GetJA2Clock(); #endif - - //Set the directory to the shadetable directory - GetFileManCurrentDirectory( DataDir ); - sprintf( ShadeTableDir, "%s\\ShadeTables", DataDir ); - if( !SetFileManCurrentDirectory( ShadeTableDir ) ) + // BF : adjust filename, instead of changing current directory (doesn't make sence with a VFS) + //////////////Set the directory to the shadetable directory + ////////////GetFileManCurrentDirectory( DataDir ); + ////////////sprintf( ShadeTableDir, "%s\\ShadeTables", DataDir ); + ////////////if( !SetFileManCurrentDirectory( ShadeTableDir ) ) + ////////////{ + //////////// AssertMsg( 0, "Can't set the directory to Data\\ShadeTable. Kris' big problem!" ); + ////////////} + CHAR8 sIgnoreShadeTables[50]; + sprintf( sIgnoreShadeTables, "%s\\%s", "ShadeTables", "IgnoreShadeTables.txt"); + //hfile = FileOpen( sIgnoreShadeTables, FILE_ACCESS_READ, FALSE ); + //if( hfile ) + //{ + // FileClose( hfile ); + if(FileExists(sIgnoreShadeTables)) { - AssertMsg( 0, "Can't set the directory to Data\\ShadeTable. Kris' big problem!" ); - } - hfile = FileOpen( "IgnoreShadeTables.txt", FILE_ACCESS_READ, FALSE ); - if( hfile ) - { - FileClose( hfile ); gfForceBuildShadeTables = TRUE; } else @@ -585,40 +590,41 @@ void BuildTileShadeTables( ) if ( gTileSurfaceArray[ uiLoop ] != NULL ) { // Don't Create shade tables if default were already used once! - #ifdef JA2EDITOR - if( gbNewTileSurfaceLoaded[ uiLoop ] || gfEditorForceShadeTableRebuild ) - #else - if( gbNewTileSurfaceLoaded[ uiLoop ] ) - #endif - { - fForceRebuildForSlot = FALSE; +#ifdef JA2EDITOR + if( gbNewTileSurfaceLoaded[ uiLoop ] || gfEditorForceShadeTableRebuild ) +#else + if( gbNewTileSurfaceLoaded[ uiLoop ] ) +#endif + { + fForceRebuildForSlot = FALSE; GetRootName( cRootFile, TileSurfaceFilenames[ uiLoop ] ); - if ( strcmp( cRootFile, "grass2" ) == 0 ) - { - fForceRebuildForSlot = TRUE; - } + if ( strcmp( cRootFile, "grass2" ) == 0 ) + { + fForceRebuildForSlot = TRUE; + } - #ifdef JA2TESTVERSION - uiNumImagesReloaded++; - #endif - RenderProgressBar( 0, uiLoop * 100 / NUMBEROFTILETYPES ); - CreateTilePaletteTables( gTileSurfaceArray[ uiLoop ]->vo, uiLoop, fForceRebuildForSlot ); - } +#ifdef JA2TESTVERSION + uiNumImagesReloaded++; +#endif + RenderProgressBar( 0, uiLoop * 100 / NUMBEROFTILETYPES ); + CreateTilePaletteTables( gTileSurfaceArray[ uiLoop ]->vo, uiLoop, fForceRebuildForSlot ); + } } } - //Restore the data directory once we are finished. - SetFileManCurrentDirectory( DataDir ); + // BF : see above + ////Restore the data directory once we are finished. + //SetFileManCurrentDirectory( DataDir ); ubLastRed = gpLightColors[0].peRed; ubLastGreen = gpLightColors[0].peGreen; ubLastBlue = gpLightColors[0].peBlue; - #ifdef JA2TESTVERSION - uiBuildShadeTableTime = GetJA2Clock() - uiStartTime; - #endif +#ifdef JA2TESTVERSION + uiBuildShadeTableTime = GetJA2Clock() - uiStartTime; +#endif } void DestroyTileShadeTables( ) diff --git a/Utils/Animated ProgressBar.cpp b/Utils/Animated ProgressBar.cpp index 9ed515ce..863e1309 100644 --- a/Utils/Animated ProgressBar.cpp +++ b/Utils/Animated ProgressBar.cpp @@ -12,6 +12,7 @@ #include "music control.h" #include "Timer Control.h" #include "sysutil.h" +#include "math.h" #endif double rStart, rEnd; @@ -23,7 +24,6 @@ double rActual; PROGRESSBAR *pBar[ MAX_PROGRESSBARS ]; -BOOLEAN gfUseLoadScreenProgressBar = FALSE; UINT16 gusLeftmostShaded = 0; extern BOOLEAN bShowSmallImage; @@ -31,7 +31,6 @@ extern BOOLEAN bShowSmallImage; void CreateLoadingScreenProgressBar() { gusLeftmostShaded = 162; - gfUseLoadScreenProgressBar = TRUE; // Special case->show small image centered if (bShowSmallImage == TRUE) @@ -56,19 +55,28 @@ void CreateLoadingScreenProgressBar() CreateProgressBar(0, 259, 683, 767, 708); } } + + SetProgressBarUseBorder(0, FALSE ); } void RemoveLoadingScreenProgressBar() { - gfUseLoadScreenProgressBar = FALSE; RemoveProgressBar( 0 ); SetFontShadow(DEFAULT_SHADOW); } +// OJW - 20090422 +void CreateProgressBarNoBorder( UINT8 ubProgressBarID, UINT16 usLeft, UINT16 usTop, UINT16 usRight, UINT16 usBottom ) +{ + CreateProgressBar( ubProgressBarID, usLeft, usTop, usRight, usBottom ); + SetProgressBarUseBorder( ubProgressBarID, FALSE ); +} + //This creates a single progress bar given the coordinates without a panel (containing a title and background). //A panel is automatically created if you specify a title using SetProgressBarTitle BOOLEAN CreateProgressBar( UINT8 ubProgressBarID, UINT16 usLeft, UINT16 usTop, UINT16 usRight, UINT16 usBottom ) { + PROGRESSBAR *pNew; //Allocate new progress bar pNew = (PROGRESSBAR*)MemAlloc( sizeof( PROGRESSBAR ) ); @@ -100,6 +108,10 @@ BOOLEAN CreateProgressBar( UINT8 ubProgressBarID, UINT16 usLeft, UINT16 usTop, U pNew->ubColorFillBlue = 0; pNew->fDisplayText = FALSE; + //OJW - 20090222 + pNew->uiFrameBuffer = FRAME_BUFFER; + pNew->fDrawBorder = TRUE; + return TRUE; } @@ -166,6 +178,27 @@ void SetProgressBarMsgAttributes( UINT32 ubID, UINT32 usFont, UINT8 ubForeColor, pCurr->ubMsgFontShadowColor = ubShadowColor; } +//OJW - 20090422 +void SetProgressBarRenderBuffer( UINT32 ubID , UINT32 uiBufferID ) +{ + PROGRESSBAR *pCurr; + Assert( ubID < MAX_PROGRESSBARS ); + pCurr = pBar[ ubID ]; + if( !pCurr ) + return; + pCurr->uiFrameBuffer = uiBufferID; +} + +void SetProgressBarUseBorder( UINT32 ubID , BOOLEAN bBorder ) +{ + PROGRESSBAR *pCurr; + Assert( ubID < MAX_PROGRESSBARS ); + pCurr = pBar[ ubID ]; + if( !pCurr ) + return; + pCurr->fDrawBorder = bBorder; +} + //When finished, the progress bar needs to be removed. void RemoveProgressBar( UINT8 ubID ) @@ -198,8 +231,8 @@ void SetRelativeStartAndEndPercentage( UINT8 ubID, UINT32 uiRelStartPerc, UINT32 if( !pCurr ) return; - pCurr->rStart = uiRelStartPerc*0.01; - pCurr->rEnd = uiRelEndPerc*0.01; + pCurr->rStart = (double)uiRelStartPerc*0.01f; + pCurr->rEnd = (double)uiRelEndPerc*0.01f; //Render the entire panel now, as it doesn't need update during the normal rendering if( pCurr->fPanel ) @@ -271,7 +304,7 @@ void RenderProgressBar( UINT8 ubID, UINT32 uiPercentage ) { rActual = pCurr->rStart+(pCurr->rEnd-pCurr->rStart)*uiPercentage*0.01; - if( rActual - pCurr->rLastActual < 0.01 ) + if( fabs(rActual - pCurr->rLastActual) < 0.01 ) { return; } @@ -283,9 +316,9 @@ void RenderProgressBar( UINT8 ubID, UINT32 uiPercentage ) { return; } - if( gfUseLoadScreenProgressBar ) + if( !pCurr->fDrawBorder ) { - ColorFillVideoSurfaceArea( FRAME_BUFFER, + ColorFillVideoSurfaceArea( pCurr->uiFrameBuffer, //FRAME_BUFFER, pCurr->usBarLeft, pCurr->usBarTop, end, pCurr->usBarBottom, Get16BPPColor(FROMRGB( pCurr->ubColorFillRed, pCurr->ubColorFillGreen, pCurr->ubColorFillBlue )) ); //if( pCurr->usBarRight > gusLeftmostShaded ) @@ -297,14 +330,14 @@ void RenderProgressBar( UINT8 ubID, UINT32 uiPercentage ) else { //Border edge of the progress bar itself in gray - ColorFillVideoSurfaceArea( FRAME_BUFFER, + ColorFillVideoSurfaceArea( pCurr->uiFrameBuffer, pCurr->usBarLeft, pCurr->usBarTop, pCurr->usBarRight, pCurr->usBarBottom, Get16BPPColor(FROMRGB(160, 160, 160)) ); //Interior of progress bar in black - ColorFillVideoSurfaceArea( FRAME_BUFFER, + ColorFillVideoSurfaceArea( pCurr->uiFrameBuffer, pCurr->usBarLeft+2, pCurr->usBarTop+2, pCurr->usBarRight-2, pCurr->usBarBottom-2, Get16BPPColor(FROMRGB( 0, 0, 0)) ); - ColorFillVideoSurfaceArea(FRAME_BUFFER, pCurr->usBarLeft+2, pCurr->usBarTop+2, end, pCurr->usBarBottom-2, Get16BPPColor(FROMRGB(72 , 155, 24))); + ColorFillVideoSurfaceArea(pCurr->uiFrameBuffer, pCurr->usBarLeft+2, pCurr->usBarTop+2, end, pCurr->usBarBottom-2, Get16BPPColor(FROMRGB(72 , 155, 24))); } InvalidateRegion( pCurr->usBarLeft, pCurr->usBarTop, pCurr->usBarRight, pCurr->usBarBottom ); ExecuteBaseDirtyRectQueue(); diff --git a/Utils/Animated ProgressBar.h b/Utils/Animated ProgressBar.h index aea495de..6c851dc5 100644 --- a/Utils/Animated ProgressBar.h +++ b/Utils/Animated ProgressBar.h @@ -25,6 +25,8 @@ typedef struct PROGRESSBAR BOOLEAN fDisplayText; BOOLEAN fUseSaveBuffer; //use the save buffer when display the text double rLastActual; + UINT32 uiFrameBuffer; + BOOLEAN fDrawBorder; }PROGRESSBAR; extern PROGRESSBAR *pBar[ MAX_PROGRESSBARS ]; @@ -75,4 +77,13 @@ void SetProgressBarColor( UINT8 ubID, UINT8 ubColorFillRed, UINT8 ubColorFillGre //Pass in TRUE to display the strings. void SetProgressBarTextDisplayFlag( UINT8 ubID, BOOLEAN fDisplayText, BOOLEAN fUseSaveBuffer, BOOLEAN fSaveScreenToFrameBuffer ); +// OJW - 20090422 +// draw the progress bar with a white border, or not +void SetProgressBarUseBorder( UINT32 ubID , BOOLEAN bBorder ); +// specify which buffer to render the bar to +void SetProgressBarRenderBuffer( UINT32 ubID , UINT32 uiBufferID ); +// create a color bar with no border +void CreateProgressBarNoBorder( UINT8 ubProgressBarID, UINT16 usLeft, UINT16 usTop, UINT16 usRight, UINT16 usBottom ); + + #endif \ No newline at end of file diff --git a/Utils/Debug Control.cpp b/Utils/Debug Control.cpp index 2a36a417..7e4a7566 100644 --- a/Utils/Debug Control.cpp +++ b/Utils/Debug Control.cpp @@ -6,7 +6,8 @@ #include "stdio.h" #endif - +#include "vfs.h" +#include "PropertyContainer.h" #ifdef _ANIMSUBSYSTEM_DEBUG @@ -59,6 +60,7 @@ void AiDbgMessage( CHAR8 *strMessage) void LiveMessage( CHAR8 *strMessage) { +#ifndef USE_VFS FILE *OutFile; if ((OutFile = fopen("Log.txt", "a+t")) != NULL) @@ -66,9 +68,14 @@ void LiveMessage( CHAR8 *strMessage) fprintf(OutFile, "%s\n", strMessage); fclose(OutFile); } +#else + static CLog& liveMsg = *CLog::Create(L"LiveLog.txt",true); + liveMsg << strMessage << CLog::endl; +#endif } void MPDebugMsg( CHAR8 *strMessage) { +#ifndef USE_VFS FILE *OutFile; if ((OutFile = fopen("MPDebug.txt", "a+t")) != NULL) @@ -76,4 +83,8 @@ void MPDebugMsg( CHAR8 *strMessage) fprintf(OutFile, "%s\n", strMessage); fclose(OutFile); } +#else + static CLog& mpMsg = *CLog::Create(L"MPDebug.txt", true); + mpMsg << strMessage << CLog::endl; +#endif } diff --git a/Utils/INIReader.cpp b/Utils/INIReader.cpp index 07f80887..849066fb 100644 --- a/Utils/INIReader.cpp +++ b/Utils/INIReader.cpp @@ -8,15 +8,31 @@ #include #include -#include //limits for float/double vars ie. DBL_MAX FLT_MAX - // Kaiden: INI reading function definitions: +#include "VFS/vfs.h" + std::stack iniErrorMessages; +template +void PushErrorMessage(std::string const& filename, + std::string const& section, + std::string const& key, + ValueType value, ValueType used_value, + ValueType minVal, ValueType maxVal) +{ + std::stringstream errMessage; + errMessage << "The value [" << section << "][" << key << "] = \"" << value << "\" " + << "in file [" << filename << "] " + << "is outside the valid range [" << minVal << " , " << maxVal << "]. " + << used_value << " will be used."; + iniErrorMessages.push(errMessage.str()); +} CIniReader::CIniReader(const STR8 szFileName) { + memset(m_szFileName,0,sizeof(m_szFileName)); +#ifndef USE_VFS // Snap: Look for the INI file in the custom Data directory. // If not there, leave at default location. if ( gCustomDataCat.FindFile(szFileName) ) { @@ -25,12 +41,18 @@ CIniReader::CIniReader(const STR8 szFileName) else { sprintf(m_szFileName, "%s\\%s", gDefaultDataCat.GetRootDir().c_str(), szFileName); } +#else + strncpy(m_szFileName,szFileName, std::min(strlen(szFileName), sizeof(m_szFileName)-1)); + m_oProps.InitFromIniFile(vfs::Path(szFileName)); +#endif } CIniReader::CIniReader(const STR8 szFileName, BOOLEAN Force_Custom_Data_Path) { + memset(m_szFileName,0,sizeof(m_szFileName)); // ary-05/05/2009 : force custom data path for potential non existing file -or- force default data path // : Also, flag file detection to allow functions to determine course of action for case of file [not found/is found]. +#ifndef USE_VFS if ( Force_Custom_Data_Path ) { if ( gCustomDataCat.FindFile(szFileName) ) @@ -55,32 +77,50 @@ CIniReader::CIniReader(const STR8 szFileName, BOOLEAN Force_Custom_Data_Path) } sprintf(m_szFileName, "%s\\%s", gDefaultDataCat.GetRootDir().c_str(), szFileName); } +#else + strncpy(m_szFileName,szFileName, std::min(strlen(szFileName), sizeof(m_szFileName)-1)); + CIniReader_File_Found = m_oProps.InitFromIniFile(vfs::Path(szFileName)); +#endif } +void CIniReader::Clear() +{ +#ifndef USE_VFS + memset(m_szFileName, 0, MAX_PATH); +#else + memset(m_szFileName, 0, MAX_PATH); + m_oProps.ClearContainer(); +#endif +} + + int CIniReader::ReadInteger(const STR8 szSection, const STR8 szKey, int iDefaultValue) { +#ifndef USE_VFS return GetPrivateProfileInt(szSection, szKey, iDefaultValue, m_szFileName); +#else + return m_oProps.GetIntProperty(szSection, szKey, iDefaultValue); +#endif } int CIniReader::ReadInteger(const STR8 szSection, const STR8 szKey, int defaultValue, int minValue, int maxValue) { +#ifndef USE_VFS int iniValueReadFromFile = GetPrivateProfileInt(szSection, szKey, defaultValue, m_szFileName); +#else + int iniValueReadFromFile = m_oProps.GetIntProperty(szSection, szKey, defaultValue); +#endif //AssertGE(iniValueReadFromFile, minValue); //AssertLE(iniValueReadFromFile, maxValue); - if (iniValueReadFromFile < minValue) { - std::stringstream errMessage; - errMessage << "The .ini setting " << szKey << "(" << iniValueReadFromFile - << ") is outside the valid range of " << minValue << " to " << maxValue - << ". " << minValue << " will be used."; - iniErrorMessages.push(errMessage.str()); + if (iniValueReadFromFile < minValue) + { + PushErrorMessage(this->m_szFileName, szSection, szKey, iniValueReadFromFile, minValue, minValue, maxValue); return minValue; - } else if (iniValueReadFromFile > maxValue) { - std::stringstream errMessage; - errMessage << "The .ini setting " << szKey << "(" << iniValueReadFromFile - << ") is outside the valid range of " << minValue << " to " << maxValue - << ". " << maxValue << " will be used."; - iniErrorMessages.push(errMessage.str()); + } + else if (iniValueReadFromFile > maxValue) + { + PushErrorMessage(this->m_szFileName, szSection, szKey, iniValueReadFromFile, maxValue, minValue, maxValue); return maxValue; } return iniValueReadFromFile; @@ -99,32 +139,28 @@ int CIniReader::ReadInteger(const STR8 szSection, const STR8 szKey, int defaultV // return fltResult; //} -DOUBLE CIniReader::ReadDouble(const STR8 szSection, const STR8 szKey, DOUBLE defaultValue, DOUBLE minValue, DOUBLE maxValue) +double CIniReader::ReadDouble(const STR8 szSection, const STR8 szKey, double defaultValue, double minValue, double maxValue) { + double iniValueReadFromFile; +#ifndef USE_VFS char szResult[255]; char szDefault[255]; - DOUBLE iniValueReadFromFile; - sprintf(szDefault, "%f", defaultValue); GetPrivateProfileString(szSection, szKey, szDefault, szResult, 255, m_szFileName); - iniValueReadFromFile = strtod(szResult, NULL); - + iniValueReadFromFile = (float) atof(szResult); +#else + iniValueReadFromFile = m_oProps.GetFloatProperty(szSection, szKey, defaultValue); +#endif //AssertGE(iniValueReadFromFile, minValue); //AssertLE(iniValueReadFromFile, maxValue); - - if (iniValueReadFromFile < minValue) { - std::stringstream errMessage; - errMessage << "The .ini setting " << szKey << "(" << iniValueReadFromFile - << ") is outside the valid range of " << minValue << " to " << maxValue - << ". " << minValue << " will be used."; - iniErrorMessages.push(errMessage.str()); + if (iniValueReadFromFile < minValue) + { + PushErrorMessage(this->m_szFileName, szSection, szKey,iniValueReadFromFile, minValue, minValue, maxValue); return minValue; - } else if (iniValueReadFromFile > maxValue) { - std::stringstream errMessage; - errMessage << "The .ini setting " << szKey << "(" << iniValueReadFromFile - << ") is outside the valid range of " << minValue << " to " << maxValue - << ". " << maxValue << " will be used."; - iniErrorMessages.push(errMessage.str()); + } + else if (iniValueReadFromFile > maxValue) + { + PushErrorMessage(this->m_szFileName, szSection, szKey, iniValueReadFromFile, maxValue, minValue, maxValue); return maxValue; } return iniValueReadFromFile; @@ -132,37 +168,36 @@ DOUBLE CIniReader::ReadDouble(const STR8 szSection, const STR8 szKey, DOUBLE def FLOAT CIniReader::ReadFloat(const STR8 szSection, const STR8 szKey, FLOAT defaultValue, FLOAT minValue, FLOAT maxValue) { + FLOAT iniValueReadFromFile; +#ifndef USE_VFS char szResult[255]; char szDefault[255]; - FLOAT iniValueReadFromFile; - sprintf(szDefault, "%f", defaultValue); GetPrivateProfileString(szSection, szKey, szDefault, szResult, 255, m_szFileName); iniValueReadFromFile = (FLOAT) atof(szResult); +#else + iniValueReadFromFile = (FLOAT) m_oProps.GetFloatProperty(szSection, szKey, (float)defaultValue); +#endif //AssertGE(iniValueReadFromFile, minValue); //AssertLE(iniValueReadFromFile, maxValue); - if (iniValueReadFromFile < minValue) { - std::stringstream errMessage; - errMessage << "The .ini setting " << szKey << "(" << iniValueReadFromFile - << ") is outside the valid range of " << minValue << " to " << maxValue - << ". " << minValue << " will be used."; - iniErrorMessages.push(errMessage.str()); + if (iniValueReadFromFile < minValue) + { + PushErrorMessage(this->m_szFileName, szSection, szKey, iniValueReadFromFile, minValue, minValue, maxValue); return minValue; - } else if (iniValueReadFromFile > maxValue) { - std::stringstream errMessage; - errMessage << "The .ini setting " << szKey << "(" << iniValueReadFromFile - << ") is outside the valid range of " << minValue << " to " << maxValue - << ". " << maxValue << " will be used."; - iniErrorMessages.push(errMessage.str()); + } + else if (iniValueReadFromFile > maxValue) + { + PushErrorMessage(this->m_szFileName, szSection, szKey, iniValueReadFromFile, maxValue, minValue, maxValue); return maxValue; } return iniValueReadFromFile; } -BOOLEAN CIniReader::ReadBoolean(const STR8 szSection, const STR8 szKey, BOOLEAN defaultValue) +BOOLEAN CIniReader::ReadBoolean(const STR8 szSection, const STR8 szKey, bool defaultValue) { +#ifndef USE_VFS char szResult[255]; char szDefault[255]; sprintf(szDefault, "%s", defaultValue? "TRUE" : "FALSE"); @@ -173,15 +208,27 @@ BOOLEAN CIniReader::ReadBoolean(const STR8 szSection, const STR8 szKey, BOOLEAN return TRUE; else if (strcmp(szResult, "FALSE") == 0) return FALSE; - +#else + utf8string str = m_oProps.GetStringProperty(szSection, szKey, L""); + if( StrCmp::Equal(str, L"true") ) + { + return TRUE; + } + else if( StrCmp::Equal(str, L"false") ) + { + return FALSE; + } + std::string szResult = str.utf8(); + char szDefault[255]; + sprintf(szDefault, "%s", defaultValue? "TRUE" : "FALSE"); +#endif std::stringstream errMessage; - errMessage << "The .ini setting " << szKey << "(" << szResult - << ") is neither TRUE nor FALSE. The value " << szDefault << " will be used."; + errMessage << "The value [" << szSection << "][" << szKey << "] = \"" << szResult << "\" " + << "in file [" << this->m_szFileName << "] is neither TRUE nor FALSE. The value " << szDefault << " will be used."; iniErrorMessages.push(errMessage.str()); return defaultValue; } - // ary-05/15/2009 : snippet on how to use CIniReader::ReadString // const STR8 test_ini_string = new char[255]; // memset(test_ini_string, 0x00, 255); @@ -189,16 +236,36 @@ BOOLEAN CIniReader::ReadBoolean(const STR8 szSection, const STR8 szKey, BOOLEAN void CIniReader::ReadString(const STR8 szSection, const STR8 szKey, const STR8 szDefaultValue, STR8 input_buffer, size_t buffer_size) { +#ifndef USE_VFS GetPrivateProfileString(szSection, szKey, szDefaultValue, input_buffer, buffer_size, m_szFileName); +#else + std::string s = m_oProps.GetStringProperty(szSection, szKey, szDefaultValue).utf8(); + int len = std::min(s.length(),buffer_size-1); + strncpy(input_buffer, s.c_str(), len); + input_buffer[len] = 0; +#endif } - +// WANNE - MP: Old version, currently used by Multiplayer +STR8 CIniReader::ReadString(const STR8 szSection, const STR8 szKey, const STR8 szDefaultValue) +{ + // >>>>> Memory Leak <<<<< + STR8 szResult = new char[255]; + memset(szResult, 0x00, 255); +#ifndef USE_VFS + GetPrivateProfileString(szSection, szKey, szDefaultValue, szResult, 255, m_szFileName); +#else + std::string s = m_oProps.GetStringProperty(szSection, szKey, szDefaultValue).utf8(); + strncpy(szResult, s.c_str(), std::min(s.length(),254)); +#endif + return szResult; +} UINT8 CIniReader::ReadUINT8(const STR8 szSection, const STR8 szKey, UINT8 defaultValue, UINT8 minValue, UINT8 maxValue) { - UINT8 iniValueReadFromFile; + iniValueReadFromFile = (UINT8) this->ReadUINT( szSection, szKey, (UINT32) defaultValue, (UINT32) minValue, (UINT32) maxValue); return iniValueReadFromFile; @@ -207,7 +274,6 @@ UINT8 CIniReader::ReadUINT8(const STR8 szSection, const STR8 szKey, UINT8 defa UINT16 CIniReader::ReadUINT16(const STR8 szSection, const STR8 szKey, UINT16 defaultValue, UINT16 minValue, UINT16 maxValue) { - UINT16 iniValueReadFromFile; iniValueReadFromFile = (UINT16) this->ReadUINT( szSection, szKey, (UINT32) defaultValue, (UINT32) minValue, (UINT32) maxValue); @@ -218,7 +284,6 @@ UINT16 CIniReader::ReadUINT16(const STR8 szSection, const STR8 szKey, UINT16 def UINT32 CIniReader::ReadUINT32(const STR8 szSection, const STR8 szKey, UINT32 defaultValue, UINT32 minValue, UINT32 maxValue) { - UINT32 iniValueReadFromFile; iniValueReadFromFile = (UINT32) this->ReadUINT( szSection, szKey, (UINT32) defaultValue, (UINT32) minValue, (UINT32) maxValue); @@ -230,7 +295,7 @@ UINT32 CIniReader::ReadUINT32(const STR8 szSection, const STR8 szKey, UINT32 def UINT32 CIniReader::ReadUINT(const STR8 szSection, const STR8 szKey, UINT32 defaultValue, UINT32 minValue, UINT32 maxValue ) { UINT32 iniValueReadFromFile; - +#ifndef USE_VFS STR8 szResult = new char[255]; STR8 szDefault = new char[255]; @@ -241,36 +306,27 @@ UINT32 CIniReader::ReadUINT(const STR8 szSection, const STR8 szKey, UINT32 defau this->ReadString (szSection , szKey , szDefault, szResult, (size_t) 255 ); iniValueReadFromFile = (UINT32) strtoul(szResult,NULL,0); - +#else + iniValueReadFromFile = (UINT32) m_oProps.GetUIntProperty(szSection, szKey, defaultValue); +#endif //AssertGE(iniValueReadFromFile, minValue); //AssertLE(iniValueReadFromFile, maxValue); if (iniValueReadFromFile < minValue) { - std::stringstream errMessage; - errMessage << "The .ini setting " << szKey << "(" << iniValueReadFromFile - << ") is outside the valid range of " << minValue << " to " << maxValue - << ". " << minValue << " will be used."; - iniErrorMessages.push(errMessage.str()); + PushErrorMessage(this->m_szFileName, szSection, szKey, iniValueReadFromFile, minValue, minValue, maxValue); iniValueReadFromFile = minValue; } else if (iniValueReadFromFile > maxValue) { - std::stringstream errMessage; - errMessage << "The .ini setting " << szKey << "(" << iniValueReadFromFile - << ") is outside the valid range of " << minValue << " to " << maxValue - << ". " << maxValue << " will be used."; - iniErrorMessages.push(errMessage.str()); + PushErrorMessage(this->m_szFileName, szSection, szKey, iniValueReadFromFile, maxValue, minValue, maxValue); iniValueReadFromFile = maxValue; } - +#ifndef USE_VFS delete [] szResult ; delete [] szDefault ; - +#endif return iniValueReadFromFile; } - - - diff --git a/Utils/INIReader.h b/Utils/INIReader.h index d2fb372d..c53e9bba 100644 --- a/Utils/INIReader.h +++ b/Utils/INIReader.h @@ -6,6 +6,8 @@ #include #include +#include "VFS/PropertyContainer.h" + // Kaiden: This will read any value out of // an INI file as long as the correct type is specified. // Methods should be fairly self explainatory @@ -27,7 +29,7 @@ public: int ReadInteger(const STR8 szSection, const STR8 szKey, int iDefaultValue); int ReadInteger(const STR8 szSection, const STR8 szKey, int defaultValue, int minValue, int maxValue); - //void testReadUINT32(void);//various limit tests of UINT and double/float handling + //UINT32 CIniReader::testReadUINT32(void);//various limit tests of UINT and double/float handling //front end functions that control type interpretation and range control, each calls internal ReadUINT UINT32 ReadUINT32(const STR8 szSection, const STR8 szKey, UINT32 defaultValue, UINT32 minValue, UINT32 maxValue); UINT16 ReadUINT16(const STR8 szSection, const STR8 szKey, UINT16 defaultValue, UINT16 minValue, UINT16 maxValue); @@ -40,13 +42,17 @@ public: DOUBLE ReadDouble(const STR8 szSection, const STR8 szKey, DOUBLE defaultValue, DOUBLE minValue, DOUBLE maxValue); FLOAT ReadFloat (const STR8 szSection, const STR8 szKey, FLOAT defaultValue, FLOAT minValue, FLOAT maxValue); - BOOLEAN ReadBoolean(const STR8 szSection, const STR8 szKey, BOOLEAN bolDefaultValue); + BOOLEAN ReadBoolean(const STR8 szSection, const STR8 szKey, bool bolDefaultValue); void ReadString(const STR8 szSection, const STR8 szKey, const STR8 szDefaultValue, STR8 input_buffer, size_t buffer_size); + // WANNE - MP: Old version, currently used by Multiplayer + STR8 ReadString(const STR8 szSection, const STR8 szKey, const STR8 szDefaultValue); + BOOLEAN Is_CIniReader_File_Found(void) {return (CIniReader_File_Found);} - + void Clear(); private: + CPropertyContainer m_oProps; char m_szFileName[MAX_PATH]; BOOLEAN CIniReader_File_Found; diff --git a/Utils/Multi Language Graphic Utils.cpp b/Utils/Multi Language Graphic Utils.cpp index 87323781..5d868844 100644 --- a/Utils/Multi Language Graphic Utils.cpp +++ b/Utils/Multi Language Graphic Utils.cpp @@ -126,6 +126,9 @@ BOOLEAN GetMLGFilename( SGPFILENAME filename, UINT16 usMLGGraphicID ) case MLG_BR: sprintf( filename, "LAPTOP\\BR.STI" ); return TRUE; + case MLG_MP_GOLDPIECEBUTTONS: + sprintf( filename, "INTERFACE\\MPGOLDPIECEBUTTONS.sti" ); + return TRUE; } #elif defined( GERMAN ) @@ -245,6 +248,9 @@ BOOLEAN GetMLGFilename( SGPFILENAME filename, UINT16 usMLGGraphicID ) case MLG_BR: sprintf( filename, "LAPTOP\\BR.STI" ); return TRUE; + case MLG_MP_GOLDPIECEBUTTONS: + sprintf( filename, "GERMAN\\MPGOLDPIECEBUTTONS_german.sti" ); + return TRUE; } #else @@ -398,6 +404,9 @@ BOOLEAN GetMLGFilename( SGPFILENAME filename, UINT16 usMLGGraphicID ) case MLG_BR: sprintf( filename, "%s\\BR_%s.STI", zLanguage, zLanguage ); break; + case MLG_MP_GOLDPIECEBUTTONS: + sprintf( filename, "%s\\MPGOLDPIECEBUTTONS_%s.sti", zLanguage, zLanguage ); + break default: return FALSE; } @@ -516,6 +525,9 @@ BOOLEAN GetMLGFilename( SGPFILENAME filename, UINT16 usMLGGraphicID ) case MLG_BR: sprintf( filename, "LAPTOP\\BR.STI" ); return TRUE; + case MLG_MP_GOLDPIECEBUTTONS: + sprintf( filename, "INTERFACE\\MPGOLDPIECEBUTTONS.sti" ); + return TRUE; } #endif diff --git a/Utils/Multi Language Graphic Utils.h b/Utils/Multi Language Graphic Utils.h index 88595b71..4270d466 100644 --- a/Utils/Multi Language Graphic Utils.h +++ b/Utils/Multi Language Graphic Utils.h @@ -40,6 +40,7 @@ enum MLG_TITLETEXT_MP, // ROMAN: Additional multiplayer text MLG_BOBBYRAYTITLE, //inshy: translation needed for russian version MLG_BR, + MLG_MP_GOLDPIECEBUTTONS, }; BOOLEAN GetMLGFilename( SGPFILENAME filename, UINT16 usMLGGraphicID ); diff --git a/Utils/STIConvert.h b/Utils/STIConvert.h index 583e47fa..39d44ba8 100644 --- a/Utils/STIConvert.h +++ b/Utils/STIConvert.h @@ -7,5 +7,6 @@ void WriteSTIFile( INT8 *pData, SGPPaletteEntry *pPalette, INT16 sWidth, INT16 sHeight, STR cOutputName, UINT32 fFlags, UINT32 uiAppDataSize ); +UINT32 ETRLECompressSubImage( UINT8 * pDest, UINT32 uiDestLen, UINT8 * p8BPPBuffer, UINT16 usWidth, UINT16 usHeight, STCISubImage * pSubImage ); #endif \ No newline at end of file diff --git a/Utils/Text Input.cpp b/Utils/Text Input.cpp index 3b0bdb62..9ccc3c73 100644 --- a/Utils/Text Input.cpp +++ b/Utils/Text Input.cpp @@ -33,6 +33,11 @@ void AddChar( UINT32 uiKey ); void RemoveChar( UINT8 ubArrayIndex ); void DeleteHilitedText(); +// OJW - 20090427 - Copy/Paste text to/from the clipboard +UINT32 PasteClipboardText(); +void CopyToClipboard( void ); + + void DoublePercentileCharacterFromStringIntoString( STR16 pSrcString, STR16 pDstString ); //All exclusive input types are handled in this function. @@ -140,6 +145,8 @@ BOOLEAN gfHiliteMode = FALSE; UINT8 gubCursorPos = 0; UINT8 gubStartHilite = 0; UINT8 gubEndHilite = 0; +// OJW - 20090426 +UINT8 gubParkingPos = 0; // start of the visible string we render in the textbox //allow the user to cut, copy, and paste just like windows. CHAR16 gszClipboardString[256]; @@ -285,6 +292,7 @@ void AddTextInputField( INT16 sLeft, INT16 sTop, INT16 sWidth, INT16 sHeight, IN //if this is the first field, then hilight it. if( gpTextInputHead == pNode ) { + gubParkingPos = 0; gubStartHilite = 0; gubEndHilite = pNode->ubStrLen; gubCursorPos = pNode->ubStrLen; @@ -443,7 +451,7 @@ void SetInputFieldStringWith8BitString( UINT8 ubField, const STR8 szNewText ) } //Allows external functions to access the strings within the fields at anytime. -void Get8BitStringFromField( UINT8 ubField, STR8 szString ) +void Get8BitStringFromField( UINT8 ubField, STR8 szString, UINT32 uiBufferSize ) { TEXTINPUTNODE *curr; curr = gpTextInputHead; @@ -451,7 +459,12 @@ void Get8BitStringFromField( UINT8 ubField, STR8 szString ) { if( curr->ubID == ubField ) { - sprintf( szString, "%S", curr->szString ); + size_t len = __min(uiBufferSize, wcslen(curr->szString)+1); +#ifdef WIN32 + sprintf_s(szString, len, "%S", curr->szString ); +#else + snprintf(szString, len, "%S", curr->szString ); +#endif return; } curr = curr->next; @@ -459,7 +472,7 @@ void Get8BitStringFromField( UINT8 ubField, STR8 szString ) szString[0] = '\0'; } -void Get16BitStringFromField( UINT8 ubField, STR16 szString ) +void Get16BitStringFromField( UINT8 ubField, STR16 szString, UINT32 uiBufferSize ) { TEXTINPUTNODE *curr; curr = gpTextInputHead; @@ -467,7 +480,8 @@ void Get16BitStringFromField( UINT8 ubField, STR16 szString ) { if( curr->ubID == ubField ) { - swprintf( szString, curr->szString ); + size_t len = __min(uiBufferSize, wcslen(curr->szString)+1); + swprintf( szString, len, curr->szString ); return; } curr = curr->next; @@ -482,7 +496,7 @@ INT32 GetNumericStrictValueFromField( UINT8 ubField ) STR16 ptr; CHAR16 str[20]; INT32 total; - Get16BitStringFromField( ubField, str ); + Get16BitStringFromField( ubField, str, 20 ); //Blank string, so return -1 if( str[0] == '\0' ) return -1; @@ -544,6 +558,7 @@ void SetActiveField( UINT8 ubField ) gpActive = curr; if( gpActive->szString ) { + gubParkingPos = 0; gubStartHilite = 0; gubEndHilite = gpActive->ubStrLen; gubCursorPos = gpActive->ubStrLen; @@ -585,6 +600,7 @@ void SelectNextField() fDone = TRUE; if( gpActive->szString ) { + gubParkingPos = 0; gubStartHilite = 0; gubEndHilite = gpActive->ubStrLen; gubCursorPos = gpActive->ubStrLen; @@ -629,6 +645,7 @@ void SelectPrevField() fDone = TRUE; if( gpActive->szString ) { + gubParkingPos = 0; gubStartHilite = 0; gubEndHilite = gpActive->ubStrLen; gubCursorPos = gpActive->ubStrLen; @@ -738,23 +755,39 @@ BOOLEAN HandleTextInput( InputAtom *Event ) } //For any number of reasons, these ALT and CTRL combination key presses //will be processed externally -#if 0 +#if 1 if( Event->usKeyState & CTRL_DOWN ) { if( Event->usParam == 'c' || Event->usParam == 'C' ) { ExecuteCopyCommand(); - return TRUE; + + // only swallow key if we did anything + if (szClipboard && wcslen(szClipboard)>0) + return TRUE; + else + return FALSE; } else if( Event->usParam == 'x' || Event->usParam == 'X' ) { ExecuteCutCommand(); - return TRUE; + + // only swallow key if we did anything + if (szClipboard && wcslen(szClipboard)>0) + return TRUE; + else + return FALSE; } else if( Event->usParam == 'v' || Event->usParam == 'V' ) { + PasteClipboardText(); ExecutePasteCommand(); - return TRUE; + + // only swallow key if we did anything + if (szClipboard && wcslen(szClipboard)>0) + return TRUE; + else + return FALSE; } } #endif @@ -1161,13 +1194,16 @@ void MouseMovedInTextRegionCallback(MOUSE_REGION *reg, INT32 reason) //Calculate the cursor position. iClickX = gusMouseXPos - reg->RegionTopLeftX; iCurrCharPos = 0; - gubCursorPos = 0; - iNextCharPos = StringPixLengthArg( pColors->usFont, 1, gpActive->szString ) / 2; + gubCursorPos = gubParkingPos; + iNextCharPos = SubstringPixLength(gpActive->szString,gubParkingPos, gubCursorPos, pColors->usFont) / 2; while( iCurrCharPos + (iNextCharPos-iCurrCharPos)/2 < iClickX && gubCursorPos < gpActive->ubStrLen ) { gubCursorPos++; + if (gubCursorPos >= gpActive->ubStrLen) + break; + iCurrCharPos = iNextCharPos; - iNextCharPos = StringPixLengthArg( pColors->usFont, gubCursorPos + 1, gpActive->szString ); + iNextCharPos = SubstringPixLength(gpActive->szString,gubParkingPos, gubCursorPos, pColors->usFont); } gubEndHilite = gubCursorPos; if( gubEndHilite != gubStartHilite ) @@ -1193,7 +1229,8 @@ void MouseClickedInTextRegionCallback(MOUSE_REGION *reg, INT32 reason) { if( curr->ubID == ubNewID ) { - gpActive = curr; + //gpActive = curr; + SetActiveField(ubNewID); break; } curr = curr->next; @@ -1204,13 +1241,15 @@ void MouseClickedInTextRegionCallback(MOUSE_REGION *reg, INT32 reason) //Calculate the cursor position. iClickX = gusMouseXPos - reg->RegionTopLeftX; iCurrCharPos = 0; - gubCursorPos = 0; - iNextCharPos = StringPixLengthArg( pColors->usFont, 1, gpActive->szString ) / 2; + gubCursorPos = gubParkingPos; + iNextCharPos = SubstringPixLength(gpActive->szString,gubParkingPos, gubCursorPos, pColors->usFont) / 2;//StringPixLengthArg( pColors->usFont, 1, gpActive->szString ) / 2; while( iCurrCharPos + (iNextCharPos-iCurrCharPos)/2 < iClickX && gubCursorPos < gpActive->ubStrLen ) { gubCursorPos++; + if (gubCursorPos >= gpActive->ubStrLen) + break; iCurrCharPos = iNextCharPos; - iNextCharPos = StringPixLengthArg( pColors->usFont, gubCursorPos + 1, gpActive->szString ); + iNextCharPos = SubstringPixLength(gpActive->szString,gubParkingPos, gubCursorPos, pColors->usFont); } gubStartHilite = gubCursorPos; //This value is the anchor gubEndHilite = gubCursorPos; //The end will move with the cursor as long as it's down. @@ -1254,6 +1293,57 @@ void RenderActiveTextField() SetFont( pColors->usFont ); usOffset = (UINT16)(( gpActive->region.RegionBottomRightY - gpActive->region.RegionTopLeftY - GetFontHeight( pColors->usFont ) ) / 2); RenderBackgroundField( gpActive ); + + // OJW - 20090426 + // Scroll string if message is larger than the textbox area + UINT16 strPixLen = StringPixLength( gpActive->szString , pColors->usFont ); + UINT16 regionXLen = gpActive->region.RegionBottomRightX - gpActive->region.RegionTopLeftX - 6; // 3 pixel gap either side + CHAR16 scrollStr[ 256 ]; // the visible portion of the string based on where we moved with the cursor + UINT16 scrollStrLen = 0; // the length of the new string to render + UINT8 scrollCursorPos = gubCursorPos; // the relative position of the cursor (for drawing only) + memset(scrollStr,0,256*sizeof(CHAR16)); + // if string to big to fit in text box... + if ( strPixLen > regionXLen ) + { + // if the cursor is left of the visible position + if (gubCursorPos < gubParkingPos) + { + // shift the visible string left + gubParkingPos = gubCursorPos; + } + // if the cursor is right of the visible position + else if (gubCursorPos > gubParkingPos) + { + // if the cursor is outside of the visible area on the right + UINT16 visPixLengthToCursor = SubstringPixLength(gpActive->szString,gubParkingPos,gubCursorPos-1, pColors->usFont); + if (visPixLengthToCursor > regionXLen) + { + // shift the visble string right + + // find out how many characters to shift the parked character right by + UINT16 diff = visPixLengthToCursor - regionXLen; + UINT16 j = 0; // the number of characters + for (j = 0; j <= (gubCursorPos - gubParkingPos); j++) + { + if (SubstringPixLength( gpActive->szString, gubParkingPos, gubParkingPos+j, pColors->usFont) >= diff) + break; + } + gubParkingPos += j + 1; + } + } + + scrollCursorPos = gubCursorPos - gubParkingPos; // set the relative cursor position (for drawing only) + scrollStrLen = gpActive->ubStrLen - gubParkingPos; + memcpy(scrollStr,&gpActive->szString[gubParkingPos], scrollStrLen*sizeof(CHAR16)); + } + else + { + scrollCursorPos = gubCursorPos; + scrollStrLen = gpActive->ubStrLen; + memcpy(scrollStr,gpActive->szString, scrollStrLen*sizeof(CHAR16)); + } + // end scroll string code + if( gfHiliteMode && gubStartHilite != gubEndHilite ) { //Some or all of the text is hilighted, so we will use a different method. UINT16 i; @@ -1261,18 +1351,19 @@ void RenderActiveTextField() //sort the hilite order. if( gubStartHilite < gubEndHilite ) { - usStart = gubStartHilite; - usEnd = gubEndHilite; + // translate hilite positions into the shifted string + usStart = gubStartHilite - gubParkingPos; + usEnd = gubEndHilite - gubParkingPos; } else { - usStart = gubEndHilite; - usEnd = gubStartHilite; + usStart = gubEndHilite - gubParkingPos; + usEnd = gubStartHilite - gubParkingPos; } //Traverse the string one character at a time, and draw the highlited part differently. - for( i = 0; i < gpActive->ubStrLen; i++ ) + for( i = 0; i < scrollStrLen; i++ ) { - uiCursorXPos = StringPixLengthArg( pColors->usFont, i, gpActive->szString ) + 3; + uiCursorXPos = StringPixLengthArg( pColors->usFont, i, scrollStr ) + 3; if( i >= usStart && i < usEnd ) { //in highlighted part of text SetFontForeground( pColors->ubHiForeColor ); @@ -1285,9 +1376,9 @@ void RenderActiveTextField() SetFontShadow( pColors->ubShadowColor ); SetFontBackground( 0 ); } - if( gpActive->szString[i] != '%' ) + if( scrollStr[i] != '%' ) { - mprintf( uiCursorXPos + gpActive->region.RegionTopLeftX, gpActive->region.RegionTopLeftY + usOffset, L"%c", gpActive->szString[i] ); + mprintf( uiCursorXPos + gpActive->region.RegionTopLeftX, gpActive->region.RegionTopLeftY + usOffset, L"%c", scrollStr[i] ); } else { @@ -1300,14 +1391,14 @@ void RenderActiveTextField() SetFontForeground( pColors->ubForeColor ); SetFontShadow( pColors->ubShadowColor ); SetFontBackground( 0 ); - DoublePercentileCharacterFromStringIntoString( gpActive->szString, str ); + DoublePercentileCharacterFromStringIntoString( scrollStr, str ); mprintf( gpActive->region.RegionTopLeftX + 3, gpActive->region.RegionTopLeftY + usOffset, str ); } //Draw the cursor in the correct position. - if( gfEditingText && gpActive->szString ) + if( gfEditingText && scrollStr ) { - DoublePercentileCharacterFromStringIntoString( gpActive->szString, str ); - uiCursorXPos = StringPixLengthArg( pColors->usFont, gubCursorPos, str ) + 2; + DoublePercentileCharacterFromStringIntoString( scrollStr, str ); + uiCursorXPos = StringPixLengthArg( pColors->usFont, scrollCursorPos, str ) + 2; if( GetJA2Clock()%1000 < 500 ) { //draw the blinking ibeam cursor during the on blink period. ColorFillVideoSurfaceArea(FRAME_BUFFER, @@ -1590,6 +1681,9 @@ void ExecuteCopyCommand() szClipboard[ ubCount-ubStart ] = gpActive->szString[ ubCount ]; } szClipboard[ ubCount-ubStart ] = L'\0'; + + // also copy this data to the W32 clipboard + CopyToClipboard(); } } @@ -1766,3 +1860,92 @@ void DoublePercentileCharacterFromStringIntoString( STR16 pSrcString, STR16 pDst } pDstString[ iDstIndex ] = 0; } + +// OJW - 20090427 - Paste clipboard text +UINT32 PasteClipboardText() +{ + // get the window handle of the window which currently holds the clipboard + //HWND wndH = GetOpenClipboardWindow(); + UINT32 iStrLen = 0; + + // open the clipboard + if(OpenClipboard(NULL)) + { + if (IsClipboardFormatAvailable(CF_UNICODETEXT)) + { + STR16 cbText = (STR16)GetClipboardData(CF_UNICODETEXT); + if (cbText != NULL) + { + iStrLen = wcslen(cbText); + if (iStrLen > 0) + { + szClipboard = (STR16)MemAlloc((iStrLen+1)*sizeof(CHAR16)); + wcscpy(szClipboard,cbText); + szClipboard[iStrLen] = L'\0'; + + // empty clipboard of data as we have copied it into the "local" clipboard + // and we will use that from now on + EmptyClipboard(); + } + } + } + + if (iStrLen == 0) + { + // no unicode text availble, try and get regular text + char* cbTextA = (char*)GetClipboardData(CF_TEXT); + if (cbTextA != NULL) + { + iStrLen = strlen(cbTextA); + if (iStrLen > 0) + { + szClipboard = (STR16)MemAlloc((iStrLen+1)*sizeof(CHAR16)); + MultiByteToWideChar( CP_UTF8, 0, cbTextA, -1, (LPWSTR)szClipboard, iStrLen);//swprintf(szClipboard,L"%S",cbTextA); + szClipboard[iStrLen] = L'\0'; + + // empty clipboard of data as we have copied it into the "local" clipboard + // and we will use that from now on + EmptyClipboard(); + } + } + } + + CloseClipboard(); + } + + // did we copy anything from the windows clipboard? + return iStrLen; +} + +// OJW - 20090427 - Copy text to the Win32 Clipboard +void CopyToClipboard( void ) +{ + if (!szClipboard || wcslen(szClipboard) <=0) + return; + + if(OpenClipboard(ghWindow)) + { + HGLOBAL clipbuffer; + STR16 writeBuffer; + + if (IsClipboardFormatAvailable(CF_UNICODETEXT)) + { + // duh + EmptyClipboard(); + + // create new DDE buffer and get exclusive lock to it + clipbuffer = GlobalAlloc(GMEM_DDESHARE, (wcslen(szClipboard)+1)*sizeof(CHAR16)); + writeBuffer = (STR16)GlobalLock(clipbuffer); + + // copy the clipboard string + wcscpy(writeBuffer, szClipboard); + + // unlock and write data to clipboard + GlobalUnlock(clipbuffer); + SetClipboardData(CF_UNICODETEXT,clipbuffer); + } + + // finish up + CloseClipboard(); + } +} \ No newline at end of file diff --git a/Utils/Text Input.h b/Utils/Text Input.h index 5c08e5af..ad6d60df 100644 --- a/Utils/Text Input.h +++ b/Utils/Text Input.h @@ -118,8 +118,8 @@ void SetInputFieldStringWith16BitString( UINT8 ubField, const STR16 szNewText ); void SetInputFieldStringWith8BitString( UINT8 ubField, const STR8 szNewText ); //Allows external functions to access the strings within the fields at anytime. -void Get8BitStringFromField( UINT8 ubField, STR8 szString ); -void Get16BitStringFromField( UINT8 ubField, STR16 szString ); +void Get8BitStringFromField( UINT8 ubField, STR8 szString, UINT32 uiBufferSize ); +void Get16BitStringFromField( UINT8 ubField, STR16 szString, UINT32 uiBufferSize ); //Utility functions for the INPUTTYPE_EXCLUSIVE_24HOURCLOCK input type. UINT16 GetExclusive24HourTimeValueFromField( UINT8 ubField ); diff --git a/Utils/Text.h b/Utils/Text.h index 79183f86..ac56275d 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -173,7 +173,6 @@ extern STR16 gzIMPColorChoosingText[]; extern STR16 sColorChoiceExplanationTexts[]; extern STR16 gzIMPDisabilityTraitText[]; //**** - enum { ANTIHACKERSTR_EXITGAME, @@ -1506,6 +1505,12 @@ enum MPH_TIMER_INVALID, MPH_ENABLECIV_TEXT, MPH_USENIV_TEXT, + MPH_OVERRIDEMAXAI_TEXT, + MPH_SYNC_CLIENT_MP_DIR, + MPH_FILE_TRANSFER_DIR_TEXT, + MPH_FILE_TRANSFER_DIR_INVALID, + MPH_FILE_TRANSFER_DIR_TEXT_ADDITIONAL, + MPH_FILE_TRANSFER_DIR_NOT_EXIST, }; extern STR16 gzMPHScreenText[]; enum @@ -1522,8 +1527,20 @@ enum MPS_ACCURACY_TEXT, MPS_DMGDONE_TEXT, MPS_DMGTAKEN_TEXT, + MPS_WAITSERVER_TEXT }; extern STR16 gzMPSScreenText[]; +enum +{ + MPC_CANCEL_TEXT, + MPC_CONNECTING_TEXT, + MPC_GETSETTINGS_TEXT, + MPC_DOWNLOADING_TEXT, + MPC_HELP1_TEXT, + MPC_HELP2_TEXT, + MPC_READY_TEXT, +}; +extern STR16 gzMPCScreenText[]; // Multiplayer Starting Edges enum { diff --git a/Utils/Utils_2005Express.vcproj b/Utils/Utils_2005Express.vcproj index d0857d33..5364d0c0 100644 --- a/Utils/Utils_2005Express.vcproj +++ b/Utils/Utils_2005Express.vcproj @@ -41,7 +41,7 @@ Name="VCCLCompilerTool" AdditionalOptions="/D "_CRT_SECURE_NO_DEPRECATE"" Optimization="0" - AdditionalIncludeDirectories="..\Multiplayer" + AdditionalIncludeDirectories="..\Multiplayer;..\VFS" PreprocessorDefinitions="WIN32;_DEBUG;_LIB" MinimalRebuild="true" BasicRuntimeChecks="3" @@ -105,7 +105,7 @@ + + + + + + + + diff --git a/Utils/Utils_VS2008.vcproj b/Utils/Utils_VS2008.vcproj index e8a1f7d9..576a656e 100644 --- a/Utils/Utils_VS2008.vcproj +++ b/Utils/Utils_VS2008.vcproj @@ -425,6 +425,14 @@ RelativePath="WordWrap.h" > + + + + + + + + diff --git a/Utils/WordWrap.cpp b/Utils/WordWrap.cpp index abfed179..c0bb54a7 100644 Binary files a/Utils/WordWrap.cpp and b/Utils/WordWrap.cpp differ diff --git a/Utils/XMLProperties.cpp b/Utils/XMLProperties.cpp new file mode 100644 index 00000000..72c6f697 --- /dev/null +++ b/Utils/XMLProperties.cpp @@ -0,0 +1,224 @@ +#include "XML_Parser.h" +#include "vfs_types.h" +#include "PropertyContainer.h" +#include "XMLWriter.h" + +#include "VFS/vfs_file_raii.h" +#include "VFS/File/vfs_file.h" + +CPropertyContainer::TagMap::TagMap() +{ + // setup default map + _map[L"Container"] = L"Container"; + _map[L"Section"] = L"Section"; + _map[L"SectionID"] = L"name"; + _map[L"Key"] = L"Key"; + _map[L"KeyID"] = L"name"; +} +utf8string const& CPropertyContainer::TagMap::Container(utf8string::char_t* container) +{ + if(container) + { + _map[L"Container"] = container; + } + return _map[L"Container"]; +} +utf8string const& CPropertyContainer::TagMap::Section(utf8string::char_t* section) +{ + if(section) + { + _map[L"Section"] = section; + } + return _map[L"Section"]; +} +utf8string const& CPropertyContainer::TagMap::SectionID(utf8string::char_t* section_id) +{ + if(section_id) + { + _map[L"SectionID"] = section_id; + } + return _map[L"SectionID"]; +} +utf8string const& CPropertyContainer::TagMap::Key(utf8string::char_t* key) +{ + if(key) + { + _map[L"Key"] = key; + } + return _map[L"Key"]; +} +utf8string const& CPropertyContainer::TagMap::KeyID(utf8string::char_t* key_id) +{ + if(key_id) + { + _map[L"KeyID"] = key_id; + } + return _map[L"KeyID"]; +} + +bool CPropertyContainer::WriteToXMLFile(vfs::Path const& sFileName, CPropertyContainer::TagMap& tagmap) +{ + XMLWriter xmlw; + + xmlw.OpenNode(tagmap.Container()); + + tSections::iterator sit = m_mapProps.begin(); + for(; sit != m_mapProps.end(); ++sit) + { + xmlw.AddAttributeToNextValue(tagmap.SectionID(),sit->first.utf8()); + xmlw.OpenNode(tagmap.Section()); + + CPropertyContainer::CSection& section = sit->second; + CPropertyContainer::CSection::tProps::iterator kit = section.mapProps.begin(); + for(; kit != section.mapProps.end(); ++kit) + { + xmlw.AddAttributeToNextValue(tagmap.KeyID(), kit->first.utf8()); + xmlw.AddValue(tagmap.Key(), kit->second.utf8()); + } + + xmlw.CloseNode(); + + } + + xmlw.CloseNode(); + + return xmlw.WriteToFile(sFileName); +} + +/*********************************************************************************/ +/*********************************************************************************/ + +class CPropertyXMLParser : public IXMLParser +{ + enum DOM_OBJECT + { + DO_ELEMENT_Container, + DO_ELEMENT_Section, + DO_ELEMENT_Key, + DO_ELEMENT_NONE, + }; +public: + CPropertyXMLParser( + CPropertyContainer& container, + CPropertyContainer::TagMap& tagmap, + XML_Parser &parser, + IXMLParser* caller = NULL) + : IXMLParser("",parser,caller), + _container(container), + _tagmap(tagmap), + current_state(DO_ELEMENT_NONE) // doesn't matter where we come from, we start fresh + {}; + virtual void OnStartElement(const XML_Char* name, const XML_Char** atts); + virtual void OnEndElement(const XML_Char* name); + virtual void OnTextElement(const XML_Char *str, int len); +private: + CPropertyContainer& _container; + CPropertyContainer::TagMap& _tagmap; + DOM_OBJECT current_state; + utf8string current_section; + utf8string current_key; +}; + + +void CPropertyXMLParser::OnStartElement(const XML_Char *name, const XML_Char **atts) +{ + utf8string utf8_name(name); + if(current_state == DO_ELEMENT_NONE && StrCmp::Equal(utf8_name,_tagmap.Container())) + { + current_state = DO_ELEMENT_Container; + } + else if(current_state == DO_ELEMENT_Container && StrCmp::Equal(utf8_name, _tagmap.Section())) + { + current_state = DO_ELEMENT_Section; + current_section = this->GetAttribute(_tagmap.SectionID().utf8().c_str(),atts); + } + else if(current_state == DO_ELEMENT_Section && StrCmp::Equal(utf8_name, _tagmap.Key())) + { + current_state = DO_ELEMENT_Key; + current_key = this->GetAttribute(_tagmap.KeyID().utf8().c_str(),atts); + } + sCharData = ""; +} + +void CPropertyXMLParser::OnEndElement(const XML_Char* name) +{ + utf8string utf8_name(name); + if(current_state == DO_ELEMENT_Key && StrCmp::Equal(utf8_name, _tagmap.Key())) + { + _container.SetStringProperty(current_section, current_key, vfs::TrimString(sCharData,0,sCharData.length())); + current_state = DO_ELEMENT_Section; + } + else if(current_state == DO_ELEMENT_Section && StrCmp::Equal(utf8_name, _tagmap.Section())) + { + current_state = DO_ELEMENT_Container; + } + else if(current_state == DO_ELEMENT_Container && StrCmp::Equal(utf8_name, _tagmap.Container())) + { + current_state = DO_ELEMENT_NONE; + } +} + +void CPropertyXMLParser::OnTextElement(const XML_Char *str, int len) +{ + if(current_state == DO_ELEMENT_Key) + { + sCharData.append(str,len); + } +} + +bool CPropertyContainer::InitFromXMLFile(vfs::Path const& sFileName, CPropertyContainer::TagMap& tagmap) +{ + vfs::tReadableFile *file = NULL; + bool delete_file = false; + try + { + vfs::COpenReadFile rfile(sFileName); + file = &rfile.file(); + rfile.release(); + } + catch(CBasicException& ex) + { + vfs::CFile* rfile = new vfs::CFile(sFileName); + delete_file = true; + file = vfs::tReadableFile::Cast(rfile); + if(!file->OpenRead()) + { + file->Close(); + delete file; + return false; + } + } + if(!file) + { + return false; + } + + vfs::UInt32 size = file->GetFileSize(); + + std::vector buffer(size+1); + + vfs::UInt32 has_read; + file->Read(&buffer[0],size,has_read); + buffer[size] = 0; + + file->Close(); + if(delete_file) delete file; + + XML_Parser parser = XML_ParserCreate(NULL); + + CPropertyXMLParser pp(*this,tagmap,parser,NULL); + pp.GrabParser(); + + if(!XML_Parse(parser, &buffer[0], size, TRUE)) + { + std::wstringstream wss; + wss << L"XML Parser Error in Groups.xml: " + << utf8string::as_utf16(XML_ErrorString(XML_GetErrorCode(parser))) + << L" at line " + << XML_GetCurrentLineNumber(parser); + THROWEXCEPTION(wss.str().c_str()); + //return false; + } + + return true; +} diff --git a/Utils/XMLWriter.cpp b/Utils/XMLWriter.cpp new file mode 100644 index 00000000..907a95e9 --- /dev/null +++ b/Utils/XMLWriter.cpp @@ -0,0 +1,160 @@ +#include "XMLWriter.h" + +#include "VFS/vfs_file_raii.h" +#include "VFS/File/vfs_file.h" + +void XMLWriter::AddValue(utf8string const& key) +{ + m_ssBuffer << Indent() << "<" << key.utf8(); + InsertAttributesIntoBuffer(); + m_ssBuffer << " />\n"; +} + +void XMLWriter::AddComment(utf8string const& comment) +{ + m_ssBuffer << Indent() << "\n"; +} + +void XMLWriter::OpenNode(utf8string const& key) +{ + std::string utf8key = key.utf8(); + m_ssBuffer << Indent() << "<" << utf8key; + InsertAttributesIntoBuffer(); + m_ssBuffer << ">\n"; + m_iIndentLevel += 1; + m_stOpenNodes.push(utf8key); +} + +bool XMLWriter::CloseNode() +{ + if(m_iIndentLevel < 1) + { + // there is nothing to close + return false; + } + if(m_stOpenNodes.empty()) + { + return false; + } + m_iIndentLevel -= 1; + m_ssBuffer << Indent() << "\n"; + m_stOpenNodes.pop(); + return true; +} + +bool XMLWriter::WriteToFile(vfs::Path const& sFileName) +{ + try + { + vfs::COpenWriteFile file(sFileName,true,true); + return WriteToFile( &file.file() ); + } + catch(CBasicException& ex) + { + vfs::CFile file(sFileName); + if(file.OpenWrite(true,true)) + { + return WriteToFile(vfs::tWriteableFile::Cast(&file)); + } + } + return false; +} + +bool XMLWriter::WriteToFile(vfs::tWriteableFile* pFile) +{ + vfs::COpenWriteFile file(pFile); + vfs::UInt32 written; + std::string &str = m_ssBuffer.str(); + if(!pFile->Write(str.c_str(), str.length() * sizeof(std::string::value_type), written)) + { + return false; + } + return true; +} + +std::string XMLWriter::Indent() +{ + std::string indent_string; + for(int i=0; i < m_iIndentLevel; ++i) + { + indent_string += "\t"; + } + return indent_string; +} + +void XMLWriter::InsertAttributesIntoBuffer() +{ + if(!m_stNextValAttributes.empty()) + { + std::vector::iterator it = m_stNextValAttributes.begin(); + for(; it != m_stNextValAttributes.end(); ++it) + { + m_ssBuffer << " " << it->first << "=\"" << it->second << "\""; + } + } + m_stNextValAttributes.clear(); +} + +std::string XMLWriter::HandleSpecialCharacters(std::string const& str) +{ + std::stringstream ss; + unsigned int current = 0, old = 0; + while( current < str.length() ) + { + char const& c = str.at(current); + if( c == '&' ) + { + ss << str.substr(old, current-old) << "&"; + old = current + 1; + } + else if( c == '<' ) + { + ss << str.substr(old, current-old) << "<"; + old = current + 1; + } + else if( c == '>' ) + { + ss << str.substr(old, current-old) << ">"; + old = current + 1; + } + else if( c == '\"' ) + { + ss << str.substr(old, current-old) << """; + old = current + 1; + } + else if( c == '\'' ) + { + ss << str.substr(old, current-old) << "'"; + old = current + 1; + } + current++; + } + if(old) + { + ss << str.substr(old, current-old); + return ss.str(); + } + return str; +} + +void testMXLWriter() +{ + //XMLWriter xmlw; + XMLWriter xmlw; + //xmlw.OpenNode(L"root"); + xmlw.OpenNode("root"); + xmlw.AddAttributeToNextValue("attr1",10); + xmlw.AddAttributeToNextValue("attr2","string"); + //xmlw.AddValue(L"val1",10); + xmlw.AddValue("val1",10); + + xmlw.AddAttributeToNextValue("node_attr",17); + xmlw.AddComment("bbb -->\n comment RGB case */ + /* Note: if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or + * RGB_ALPHA (in which case need_expand is superfluous anyway), the + * background color might actually be gray yet not be flagged as such. + * This is not a problem for the current code, which uses + * PNG_BACKGROUND_IS_GRAY only to decide when to do the + * png_do_gray_to_rgb() transformation. + */ + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + !(color_type & PNG_COLOR_MASK_COLOR)) + { + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + } else if ((png_ptr->transformations & PNG_BACKGROUND) && + !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_GRAY_TO_RGB) && + png_ptr->background.red == png_ptr->background.green && + png_ptr->background.red == png_ptr->background.blue) + { + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + png_ptr->background.gray = png_ptr->background.red; + } +#endif + + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_EXPAND)) + { + if (!(color_type & PNG_COLOR_MASK_COLOR)) /* i.e., GRAY or GRAY_ALPHA */ + { + /* expand background and tRNS chunks */ + switch (png_ptr->bit_depth) + { + case 1: + png_ptr->background.gray *= (png_uint_16)0xff; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0xff; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + case 2: + png_ptr->background.gray *= (png_uint_16)0x55; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0x55; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + case 4: + png_ptr->background.gray *= (png_uint_16)0x11; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0x11; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + case 8: + case 16: + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + break; + } + } + else if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->background.red = + png_ptr->palette[png_ptr->background.index].red; + png_ptr->background.green = + png_ptr->palette[png_ptr->background.index].green; + png_ptr->background.blue = + png_ptr->palette[png_ptr->background.index].blue; + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + { +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) +#endif + { + /* invert the alpha channel (in tRNS) unless the pixels are + going to be expanded, in which case leave it for later */ + int i, istop; + istop=(int)png_ptr->num_trans; + for (i=0; itrans[i] = (png_byte)(255 - png_ptr->trans[i]); + } + } +#endif + + } + } +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) + png_ptr->background_1 = png_ptr->background; +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + + if ((color_type == PNG_COLOR_TYPE_PALETTE && png_ptr->num_trans != 0) + && (fabs(png_ptr->screen_gamma * png_ptr->gamma - 1.0) + < PNG_GAMMA_THRESHOLD)) + { + int i, k; + k=0; + for (i=0; inum_trans; i++) + { + if (png_ptr->trans[i] != 0 && png_ptr->trans[i] != 0xff) + k=1; /* partial transparency is present */ + } + if (k == 0) + png_ptr->transformations &= ~PNG_GAMMA; + } + + if ((png_ptr->transformations & (PNG_GAMMA | PNG_RGB_TO_GRAY)) && + png_ptr->gamma != 0.0) + { + png_build_gamma_table(png_ptr); +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + /* could skip if no transparency and + */ + png_color back, back_1; + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + double g, gs; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = 1.0; + break; + case PNG_BACKGROUND_GAMMA_FILE: + g = 1.0 / (png_ptr->gamma); + gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + break; + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = 1.0 / (png_ptr->background_gamma); + gs = 1.0 / (png_ptr->background_gamma * + png_ptr->screen_gamma); + break; + default: + g = 1.0; /* back_1 */ + gs = 1.0; /* back */ + } + + if ( fabs(gs - 1.0) < PNG_GAMMA_THRESHOLD) + { + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + } + else + { + back.red = (png_byte)(pow( + (double)png_ptr->background.red/255, gs) * 255.0 + .5); + back.green = (png_byte)(pow( + (double)png_ptr->background.green/255, gs) * 255.0 + .5); + back.blue = (png_byte)(pow( + (double)png_ptr->background.blue/255, gs) * 255.0 + .5); + } + + back_1.red = (png_byte)(pow( + (double)png_ptr->background.red/255, g) * 255.0 + .5); + back_1.green = (png_byte)(pow( + (double)png_ptr->background.green/255, g) * 255.0 + .5); + back_1.blue = (png_byte)(pow( + (double)png_ptr->background.blue/255, g) * 255.0 + .5); + } + for (i = 0; i < num_palette; i++) + { + if (i < (int)png_ptr->num_trans && png_ptr->trans[i] != 0xff) + { + if (png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else /* if (png_ptr->trans[i] != 0xff) */ + { + png_byte v, w; + + v = png_ptr->gamma_to_1[palette[i].red]; + png_composite(w, v, png_ptr->trans[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].green]; + png_composite(w, v, png_ptr->trans[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].blue]; + png_composite(w, v, png_ptr->trans[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + /* Prevent the transformations being done again, and make sure + * that the now spurious alpha channel is stripped - the code + * has just reduced background composition and gamma correction + * to a simple alpha channel strip. + */ + png_ptr->transformations &= ~PNG_BACKGROUND; + png_ptr->transformations &= ~PNG_GAMMA; + png_ptr->transformations |= PNG_STRIP_ALPHA; + } + /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ + else + /* color_type != PNG_COLOR_TYPE_PALETTE */ + { + double m = (double)(((png_uint_32)1 << png_ptr->bit_depth) - 1); + double g = 1.0; + double gs = 1.0; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = 1.0; + break; + case PNG_BACKGROUND_GAMMA_FILE: + g = 1.0 / (png_ptr->gamma); + gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + break; + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = 1.0 / (png_ptr->background_gamma); + gs = 1.0 / (png_ptr->background_gamma * + png_ptr->screen_gamma); + break; + } + + png_ptr->background_1.gray = (png_uint_16)(pow( + (double)png_ptr->background.gray / m, g) * m + .5); + png_ptr->background.gray = (png_uint_16)(pow( + (double)png_ptr->background.gray / m, gs) * m + .5); + + if ((png_ptr->background.red != png_ptr->background.green) || + (png_ptr->background.red != png_ptr->background.blue) || + (png_ptr->background.red != png_ptr->background.gray)) + { + /* RGB or RGBA with color background */ + png_ptr->background_1.red = (png_uint_16)(pow( + (double)png_ptr->background.red / m, g) * m + .5); + png_ptr->background_1.green = (png_uint_16)(pow( + (double)png_ptr->background.green / m, g) * m + .5); + png_ptr->background_1.blue = (png_uint_16)(pow( + (double)png_ptr->background.blue / m, g) * m + .5); + png_ptr->background.red = (png_uint_16)(pow( + (double)png_ptr->background.red / m, gs) * m + .5); + png_ptr->background.green = (png_uint_16)(pow( + (double)png_ptr->background.green / m, gs) * m + .5); + png_ptr->background.blue = (png_uint_16)(pow( + (double)png_ptr->background.blue / m, gs) * m + .5); + } + else + { + /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */ + png_ptr->background_1.red = png_ptr->background_1.green + = png_ptr->background_1.blue = png_ptr->background_1.gray; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + } + } + } + else + /* transformation does not include PNG_BACKGROUND */ +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + + /* Done the gamma correction. */ + png_ptr->transformations &= ~PNG_GAMMA; + } + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + else +#endif +#endif /* PNG_READ_GAMMA_SUPPORTED && PNG_FLOATING_POINT_SUPPORTED */ +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + /* No GAMMA transformation */ + if ((png_ptr->transformations & PNG_BACKGROUND) && + (color_type == PNG_COLOR_TYPE_PALETTE)) + { + int i; + int istop = (int)png_ptr->num_trans; + png_color back; + png_colorp palette = png_ptr->palette; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < istop; i++) + { + if (png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else if (png_ptr->trans[i] != 0xff) + { + /* The png_composite() macro is defined in png.h */ + png_composite(palette[i].red, palette[i].red, + png_ptr->trans[i], back.red); + png_composite(palette[i].green, palette[i].green, + png_ptr->trans[i], back.green); + png_composite(palette[i].blue, palette[i].blue, + png_ptr->trans[i], back.blue); + } + } + + /* Handled alpha, still need to strip the channel. */ + png_ptr->transformations &= ~PNG_BACKGROUND; + png_ptr->transformations |= PNG_STRIP_ALPHA; + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + +#if defined(PNG_READ_SHIFT_SUPPORTED) + if ((png_ptr->transformations & PNG_SHIFT) && + (color_type == PNG_COLOR_TYPE_PALETTE)) + { + png_uint_16 i; + png_uint_16 istop = png_ptr->num_palette; + int sr = 8 - png_ptr->sig_bit.red; + int sg = 8 - png_ptr->sig_bit.green; + int sb = 8 - png_ptr->sig_bit.blue; + + if (sr < 0 || sr > 8) + sr = 0; + if (sg < 0 || sg > 8) + sg = 0; + if (sb < 0 || sb > 8) + sb = 0; + for (i = 0; i < istop; i++) + { + png_ptr->palette[i].red >>= sr; + png_ptr->palette[i].green >>= sg; + png_ptr->palette[i].blue >>= sb; + } + } +#endif /* PNG_READ_SHIFT_SUPPORTED */ + } +#if !defined(PNG_READ_GAMMA_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) \ + && !defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr) + return; +#endif +} + +/* Modify the info structure to reflect the transformations. The + * info should be updated so a PNG file could be written with it, + * assuming the transformations result in valid PNG data. + */ +void /* PRIVATE */ +png_read_transform_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_transform_info"); +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans && + (png_ptr->transformations & PNG_EXPAND_tRNS)) + info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + else + info_ptr->color_type = PNG_COLOR_TYPE_RGB; + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + else + { + if (png_ptr->num_trans) + { + if (png_ptr->transformations & PNG_EXPAND_tRNS) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; +#if 0 /* Removed from libpng-1.2.27 */ + else + info_ptr->color_type |= PNG_COLOR_MASK_COLOR; +#endif + } + if (info_ptr->bit_depth < 8) + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + } +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; + info_ptr->num_trans = 0; + info_ptr->background = png_ptr->background; + } +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->transformations & PNG_GAMMA) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = png_ptr->gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = png_ptr->int_gamma; +#endif + } +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16)) + info_ptr->bit_depth = 8; +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + info_ptr->color_type |= PNG_COLOR_MASK_COLOR; +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR; +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + if (png_ptr->transformations & PNG_DITHER) + { + if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && + png_ptr->palette_lookup && info_ptr->bit_depth == 8) + { + info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; + } + } +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) + if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8)) + info_ptr->bit_depth = 8; +#endif + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) + info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; +#endif + + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + +#if defined(PNG_READ_FILLER_SUPPORTED) + /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */ + if ((png_ptr->transformations & PNG_FILLER) && + ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_GRAY))) + { + info_ptr->channels++; + /* if adding a true alpha channel not just filler */ +#if !defined(PNG_1_0_X) + if (png_ptr->transformations & PNG_ADD_ALPHA) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; +#endif + } +#endif + +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \ +defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + if (info_ptr->bit_depth < png_ptr->user_transform_depth) + info_ptr->bit_depth = png_ptr->user_transform_depth; + if (info_ptr->channels < png_ptr->user_transform_channels) + info_ptr->channels = png_ptr->user_transform_channels; + } +#endif + + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * + info_ptr->bit_depth); + + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width); + +#if !defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr) + return; +#endif +} + +/* Transform the row. The order of transformations is significant, + * and is very touchy. If you add a transformation, take care to + * decide how it fits in with the other transformations here. + */ +void /* PRIVATE */ +png_do_read_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_read_transformations"); + if (png_ptr->row_buf == NULL) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[50]; + + png_snprintf2(msg, 50, + "NULL row buffer for row %ld, pass %d", (long)png_ptr->row_number, + png_ptr->pass); + png_error(png_ptr, msg); +#else + png_error(png_ptr, "NULL row buffer"); +#endif + } +#ifdef PNG_WARN_UNINITIALIZED_ROW + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + /* Application has failed to call either png_read_start_image() + * or png_read_update_info() after setting transforms that expand + * pixels. This check added to libpng-1.2.19 */ +#if (PNG_WARN_UNINITIALIZED_ROW==1) + png_error(png_ptr, "Uninitialized row"); +#else + png_warning(png_ptr, "Uninitialized row"); +#endif +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->row_info.color_type == PNG_COLOR_TYPE_PALETTE) + { + png_do_expand_palette(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->palette, png_ptr->trans, png_ptr->num_trans); + } + else + { + if (png_ptr->num_trans && + (png_ptr->transformations & PNG_EXPAND_tRNS)) + png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->trans_values)); + else + png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, + NULL); + } + } +#endif + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + PNG_FLAG_FILLER_AFTER | (png_ptr->flags & PNG_FLAG_STRIP_ALPHA)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + { + int rgb_error = + png_do_rgb_to_gray(png_ptr, &(png_ptr->row_info), png_ptr->row_buf + 1); + if (rgb_error) + { + png_ptr->rgb_to_gray_status=1; + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == + PNG_RGB_TO_GRAY_WARN) + png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == + PNG_RGB_TO_GRAY_ERR) + png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + } + } +#endif + +/* +From Andreas Dilger e-mail to png-implement, 26 March 1998: + + In most cases, the "simple transparency" should be done prior to doing + gray-to-RGB, or you will have to test 3x as many bytes to check if a + pixel is transparent. You would also need to make sure that the + transparency information is upgraded to RGB. + + To summarize, the current flow is: + - Gray + simple transparency -> compare 1 or 2 gray bytes and composite + with background "in place" if transparent, + convert to RGB if necessary + - Gray + alpha -> composite with gray background and remove alpha bytes, + convert to RGB if necessary + + To support RGB backgrounds for gray images we need: + - Gray + simple transparency -> convert to RGB + simple transparency, compare + 3 or 6 bytes and composite with background + "in place" if transparent (3x compare/pixel + compared to doing composite with gray bkgrnd) + - Gray + alpha -> convert to RGB + alpha, composite with background and + remove alpha bytes (3x float operations/pixel + compared with composite on gray background) + + Greg's change will do this. The reason it wasn't done before is for + performance, as this increases the per-pixel operations. If we would check + in advance if the background was gray or RGB, and position the gray-to-RGB + transform appropriately, then it would save a lot of work/time. + */ + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + /* if gray -> RGB, do so now only if background is non-gray; else do later + * for performance reasons */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if ((png_ptr->transformations & PNG_BACKGROUND) && + ((png_ptr->num_trans != 0 ) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) + png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->trans_values), &(png_ptr->background) +#if defined(PNG_READ_GAMMA_SUPPORTED) + , &(png_ptr->background_1), + png_ptr->gamma_table, png_ptr->gamma_from_1, + png_ptr->gamma_to_1, png_ptr->gamma_16_table, + png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1, + png_ptr->gamma_shift +#endif +); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) + if ((png_ptr->transformations & PNG_GAMMA) && +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + !((png_ptr->transformations & PNG_BACKGROUND) && + ((png_ptr->num_trans != 0) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) && +#endif + (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)) + png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->gamma_table, png_ptr->gamma_16_table, + png_ptr->gamma_shift); +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + if (png_ptr->transformations & PNG_16_TO_8) + png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + if (png_ptr->transformations & PNG_DITHER) + { + png_do_dither((png_row_infop)&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->palette_lookup, png_ptr->dither_index); + if (png_ptr->row_info.rowbytes == (png_uint_32)0) + png_error(png_ptr, "png_do_dither returned rowbytes=0"); + } +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_do_unshift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_do_unpack(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + /* if gray -> RGB, do so now only if we did not do so above */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + (png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_do_read_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->filler, png_ptr->flags); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_read_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_read_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + if (png_ptr->read_user_transform_fn != NULL) + (*(png_ptr->read_user_transform_fn)) /* user read transform function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if (png_ptr->user_transform_depth) + png_ptr->row_info.bit_depth = png_ptr->user_transform_depth; + if (png_ptr->user_transform_channels) + png_ptr->row_info.channels = png_ptr->user_transform_channels; +#endif + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + } +#endif + +} + +#if defined(PNG_READ_PACK_SUPPORTED) +/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel, + * without changing the actual values. Thus, if you had a row with + * a bit depth of 1, you would end up with bytes that only contained + * the numbers 0 or 1. If you would rather they contain 0 and 255, use + * png_do_shift() after this. + */ +void /* PRIVATE */ +png_do_unpack(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_unpack"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && row_info->bit_depth < 8) +#else + if (row_info->bit_depth < 8) +#endif + { + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + switch (row_info->bit_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 3); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x01); + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + + png_bytep sp = row + (png_size_t)((row_width - 1) >> 2); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x03); + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 1); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x0f); + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift = 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_width * row_info->channels; + } +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) +/* Reverse the effects of png_do_shift. This routine merely shifts the + * pixels back to their significant bits values. Thus, if you have + * a row of bit depth 8, but only 5 are significant, this will shift + * the values back to 0 through 31. + */ +void /* PRIVATE */ +png_do_unshift(png_row_infop row_info, png_bytep row, png_color_8p sig_bits) +{ + png_debug(1, "in png_do_unshift"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && sig_bits != NULL && +#endif + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift[4]; + int channels = 0; + int c; + png_uint_16 value = 0; + png_uint_32 row_width = row_info->width; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift[channels++] = row_info->bit_depth - sig_bits->red; + shift[channels++] = row_info->bit_depth - sig_bits->green; + shift[channels++] = row_info->bit_depth - sig_bits->blue; + } + else + { + shift[channels++] = row_info->bit_depth - sig_bits->gray; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift[channels++] = row_info->bit_depth - sig_bits->alpha; + } + + for (c = 0; c < channels; c++) + { + if (shift[c] <= 0) + shift[c] = 0; + else + value = 1; + } + + if (!value) + return; + + switch (row_info->bit_depth) + { + case 2: + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (bp = row, i = 0; i < istop; i++) + { + *bp >>= 1; + *bp++ &= 0x55; + } + break; + } + case 4: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_byte mask = (png_byte)((((int)0xf0 >> shift[0]) & (int)0xf0) | + (png_byte)((int)0xf >> shift[0])); + + for (i = 0; i < istop; i++) + { + *bp >>= shift[0]; + *bp++ &= mask; + } + break; + } + case 8: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = row_width * channels; + + for (i = 0; i < istop; i++) + { + *bp++ >>= shift[i%channels]; + } + break; + } + case 16: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_width; + + for (i = 0; i < istop; i++) + { + value = (png_uint_16)((*bp << 8) + *(bp + 1)); + value >>= shift[i%channels]; + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + break; + } + } + } +} +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* chop rows of bit depth 16 down to 8 */ +void /* PRIVATE */ +png_do_chop(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_chop"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && row_info->bit_depth == 16) +#else + if (row_info->bit_depth == 16) +#endif + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + png_uint_32 istop = row_info->width * row_info->channels; + + for (i = 0; i> 8)) >> 8; + * + * Approximate calculation with shift/add instead of multiply/divide: + * *dp = ((((png_uint_32)(*sp) << 8) | + * (png_uint_32)((int)(*(sp + 1)) - *sp)) + 128) >> 8; + * + * What we actually do to avoid extra shifting and conversion: + */ + + *dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0); +#else + /* Simply discard the low order byte */ + *dp = *sp; +#endif + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_info->width * row_info->channels; + } +} +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_swap_alpha"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from RGBA to ARGB */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + /* This converts from RRGGBBAA to AARRGGBB */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from GA to AG */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + /* This converts from GGAA to AAGG */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } + } + } +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_invert_alpha"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=3; + dp=sp; + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=6; + dp=sp; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = *(--sp); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); +/* + *(--dp) = *(--sp); + *(--dp) = *(--sp); +*/ + sp-=2; + dp=sp; + } + } + } + } +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) +/* Add filler channel if we have RGB color */ +void /* PRIVATE */ +png_do_read_filler(png_row_infop row_info, png_bytep row, + png_uint_32 filler, png_uint_32 flags) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_byte hi_filler = (png_byte)((filler>>8) & 0xff); + png_byte lo_filler = (png_byte)(filler & 0xff); + + png_debug(1, "in png_do_read_filler"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if (row_info->bit_depth == 8) + { + /* This changes the data from G to GX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + /* This changes the data from G to XG */ + else + { + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + } + else if (row_info->bit_depth == 16) + { + /* This changes the data from GG to GGXX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + /* This changes the data from GG to XXGG */ + else + { + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + } /* COLOR_TYPE == GRAY */ + else if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if (row_info->bit_depth == 8) + { + /* This changes the data from RGB to RGBX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + /* This changes the data from RGB to XRGB */ + else + { + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + else if (row_info->bit_depth == 16) + { + /* This changes the data from RRGGBB to RRGGBBXX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + /* This changes the data from RRGGBB to XXRRGGBB */ + else + { + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + } + } /* COLOR_TYPE == RGB */ +} +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +/* expand grayscale files to RGB, with or without alpha */ +void /* PRIVATE */ +png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_debug(1, "in png_do_gray_to_rgb"); + if (row_info->bit_depth >= 8 && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + !(row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if (row_info->bit_depth == 8) + { + png_bytep sp = row + (png_size_t)row_width - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + else + { + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + else + { + png_bytep sp = row + (png_size_t)row_width * 4 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + row_info->channels += (png_byte)2; + row_info->color_type |= PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } +} +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +/* reduce RGB files to grayscale, with or without alpha + * using the equation given in Poynton's ColorFAQ at + * (THIS LINK IS DEAD June 2008) + * New link: + * + * Charles Poynton poynton at poynton.com + * + * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + * + * We approximate this with + * + * Y = 0.21268 * R + 0.7151 * G + 0.07217 * B + * + * which can be expressed with integers as + * + * Y = (6969 * R + 23434 * G + 2365 * B)/32768 + * + * The calculation is to be done in a linear colorspace. + * + * Other integer coefficents can be used via png_set_rgb_to_gray(). + */ +int /* PRIVATE */ +png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row) + +{ + png_uint_32 i; + + png_uint_32 row_width = row_info->width; + int rgb_error = 0; + + png_debug(1, "in png_do_rgb_to_gray"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; + png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; + png_uint_32 bc = png_ptr->rgb_to_gray_blue_coeff; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + + for (i = 0; i < row_width; i++) + { + png_byte red = png_ptr->gamma_to_1[*(sp++)]; + png_byte green = png_ptr->gamma_to_1[*(sp++)]; + png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + if (red != green || red != blue) + { + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1[ + (rc*red + gc*green + bc*blue)>>15]; + } + else + *(dp++) = *(sp - 1); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + if (red != green || red != blue) + { + rgb_error |= 1; + *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); + } + else + *(dp++) = *(sp - 1); + } + } + } + + else /* RGB bit_depth == 16 */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_to_1 != NULL && + png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if (red == green && red == blue) + w = red; + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> + png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> + png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 + + bc*blue_1)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if (red != green || red != blue) + rgb_error |= 1; + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + } + } + } + } + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = png_ptr->gamma_to_1[*(sp++)]; + png_byte green = png_ptr->gamma_to_1[*(sp++)]; + png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + if (red != green || red != blue) + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1 + [(rc*red + gc*green + bc*blue)>>15]; + *(dp++) = *(sp++); /* alpha */ + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + if (red != green || red != blue) + rgb_error |= 1; + *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = *(sp++); /* alpha */ + } + } + } + else /* RGBA bit_depth == 16 */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_to_1 != NULL && + png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if (red == green && red == blue) + w = red; + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> + png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> + png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc * red_1 + + gc * green_1 + bc * blue_1)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + red = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + if (red != green || red != blue) + rgb_error |= 1; + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); + } + } + } + } + row_info->channels -= (png_byte)2; + row_info->color_type &= ~PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + return rgb_error; +} +#endif + +/* Build a grayscale palette. Palette is assumed to be 1 << bit_depth + * large of png_color. This lets grayscale images be treated as + * paletted. Most useful for gamma correction and simplification + * of code. + */ +void PNGAPI +png_build_grayscale_palette(int bit_depth, png_colorp palette) +{ + int num_palette; + int color_inc; + int i; + int v; + + png_debug(1, "in png_do_build_grayscale_palette"); + if (palette == NULL) + return; + + switch (bit_depth) + { + case 1: + num_palette = 2; + color_inc = 0xff; + break; + case 2: + num_palette = 4; + color_inc = 0x55; + break; + case 4: + num_palette = 16; + color_inc = 0x11; + break; + case 8: + num_palette = 256; + color_inc = 1; + break; + default: + num_palette = 0; + color_inc = 0; + break; + } + + for (i = 0, v = 0; i < num_palette; i++, v += color_inc) + { + palette[i].red = (png_byte)v; + palette[i].green = (png_byte)v; + palette[i].blue = (png_byte)v; + } +} + +/* This function is currently unused. Do we really need it? */ +#if defined(PNG_READ_DITHER_SUPPORTED) && defined(PNG_CORRECT_PALETTE_SUPPORTED) +void /* PRIVATE */ +png_correct_palette(png_structp png_ptr, png_colorp palette, + int num_palette) +{ + png_debug(1, "in png_correct_palette"); +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + if (png_ptr->transformations & (PNG_GAMMA | PNG_BACKGROUND)) + { + png_color back, back_1; + + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + double g; + + g = 1.0 / (png_ptr->background_gamma * png_ptr->screen_gamma); + + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_SCREEN || + fabs(g - 1.0) < PNG_GAMMA_THRESHOLD) + { + back.red = png_ptr->background.red; + back.green = png_ptr->background.green; + back.blue = png_ptr->background.blue; + } + else + { + back.red = + (png_byte)(pow((double)png_ptr->background.red/255, g) * + 255.0 + 0.5); + back.green = + (png_byte)(pow((double)png_ptr->background.green/255, g) * + 255.0 + 0.5); + back.blue = + (png_byte)(pow((double)png_ptr->background.blue/255, g) * + 255.0 + 0.5); + } + + g = 1.0 / png_ptr->background_gamma; + + back_1.red = + (png_byte)(pow((double)png_ptr->background.red/255, g) * + 255.0 + 0.5); + back_1.green = + (png_byte)(pow((double)png_ptr->background.green/255, g) * + 255.0 + 0.5); + back_1.blue = + (png_byte)(pow((double)png_ptr->background.blue/255, g) * + 255.0 + 0.5); + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_uint_32 i; + + for (i = 0; i < (png_uint_32)num_palette; i++) + { + if (i < png_ptr->num_trans && png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else if (i < png_ptr->num_trans && png_ptr->trans[i] != 0xff) + { + png_byte v, w; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].red]; + png_composite(w, v, png_ptr->trans[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].green]; + png_composite(w, v, png_ptr->trans[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].blue]; + png_composite(w, v, png_ptr->trans[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + else + { + int i; + + for (i = 0; i < num_palette; i++) + { + if (palette[i].red == (png_byte)png_ptr->trans_values.gray) + { + palette[i] = back; + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + } + else +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->transformations & PNG_GAMMA) + { + int i; + + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + else +#endif +#endif +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_color back; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < (int)png_ptr->num_trans; i++) + { + if (png_ptr->trans[i] == 0) + { + palette[i].red = back.red; + palette[i].green = back.green; + palette[i].blue = back.blue; + } + else if (png_ptr->trans[i] != 0xff) + { + png_composite(palette[i].red, png_ptr->palette[i].red, + png_ptr->trans[i], back.red); + png_composite(palette[i].green, png_ptr->palette[i].green, + png_ptr->trans[i], back.green); + png_composite(palette[i].blue, png_ptr->palette[i].blue, + png_ptr->trans[i], back.blue); + } + } + } + else /* assume grayscale palette (what else could it be?) */ + { + int i; + + for (i = 0; i < num_palette; i++) + { + if (i == (png_byte)png_ptr->trans_values.gray) + { + palette[i].red = (png_byte)png_ptr->background.red; + palette[i].green = (png_byte)png_ptr->background.green; + palette[i].blue = (png_byte)png_ptr->background.blue; + } + } + } + } +#endif +} +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +/* Replace any alpha or transparency with the supplied background color. + * "background" is already in the screen gamma, while "background_1" is + * at a gamma of 1.0. Paletted files have already been taken care of. + */ +void /* PRIVATE */ +png_do_background(png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background +#if defined(PNG_READ_GAMMA_SUPPORTED) + , png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift +#endif + ) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + int shift; + + png_debug(1, "in png_do_background"); + if (background != NULL && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) || + (row_info->color_type != PNG_COLOR_TYPE_PALETTE && trans_values))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_GRAY: + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row; + shift = 7; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x01) + == trans_values->gray) + { + *sp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 7; + sp++; + } + else + shift--; + } + break; + } + case 2: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == trans_values->gray) + { + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + else + { + png_byte p = (png_byte)((*sp >> shift) & 0x03); + png_byte g = (png_byte)((gamma_table [p | (p << 2) | + (p << 4) | (p << 6)] >> 6) & 0x03); + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(g << shift); + } + if (!shift) + { + shift = 6; + sp++; + } + else + shift -= 2; + } + } + else +#endif + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == trans_values->gray) + { + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 6; + sp++; + } + else + shift -= 2; + } + } + break; + } + case 4: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == trans_values->gray) + { + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + else + { + png_byte p = (png_byte)((*sp >> shift) & 0x0f); + png_byte g = (png_byte)((gamma_table[p | + (p << 4)] >> 4) & 0x0f); + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(g << shift); + } + if (!shift) + { + shift = 4; + sp++; + } + else + shift -= 4; + } + } + else +#endif + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == trans_values->gray) + { + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 4; + sp++; + } + else + shift -= 4; + } + } + break; + } + case 8: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == trans_values->gray) + { + *sp = (png_byte)background->gray; + } + else + { + *sp = gamma_table[*sp]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == trans_values->gray) + { + *sp = (png_byte)background->gray; + } + } + } + break; + } + case 16: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + if (v == trans_values->gray) + { + /* background is already in screen gamma */ + *sp = (png_byte)((background->gray >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->gray & 0xff); + } + else + { + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + if (v == trans_values->gray) + { + *sp = (png_byte)((background->gray >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->gray & 0xff); + } + } + } + break; + } + } + break; + } + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == trans_values->red && + *(sp + 1) == trans_values->green && + *(sp + 2) == trans_values->blue) + { + *sp = (png_byte)background->red; + *(sp + 1) = (png_byte)background->green; + *(sp + 2) = (png_byte)background->blue; + } + else + { + *sp = gamma_table[*sp]; + *(sp + 1) = gamma_table[*(sp + 1)]; + *(sp + 2) = gamma_table[*(sp + 2)]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == trans_values->red && + *(sp + 1) == trans_values->green && + *(sp + 2) == trans_values->blue) + { + *sp = (png_byte)background->red; + *(sp + 1) = (png_byte)background->green; + *(sp + 2) = (png_byte)background->blue; + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + if (r == trans_values->red && g == trans_values->green && + b == trans_values->blue) + { + /* background is already in screen gamma */ + *sp = (png_byte)((background->red >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->red & 0xff); + *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(sp + 3) = (png_byte)(background->green & 0xff); + *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(sp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp+1)); + png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + + if (r == trans_values->red && g == trans_values->green && + b == trans_values->blue) + { + *sp = (png_byte)((background->red >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->red & 0xff); + *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(sp + 3) = (png_byte)(background->green & 0xff); + *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(sp + 5) = (png_byte)(background->blue & 0xff); + } + } + } + } + break; + } + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 2, dp++) + { + png_uint_16 a = *(sp + 1); + + if (a == 0xff) + { + *dp = gamma_table[*sp]; + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)background->gray; + } + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, background_1->gray); + *dp = gamma_from_1[w]; + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 2, dp++) + { + png_byte a = *(sp + 1); + + if (a == 0xff) + { + *dp = *sp; + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) + { + *dp = (png_byte)background->gray; + } + else + { + png_composite(*dp, *sp, a, background_1->gray); + } +#else + *dp = (png_byte)background->gray; +#endif + } + } + } + else /* if (png_ptr->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 2) + { + png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) +#else + else +#endif + { + /* background is already in screen gamma */ + *dp = (png_byte)((background->gray >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->gray & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else + { + png_uint_16 g, v, w; + + g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(v, g, a, background_1->gray); + w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8]; + *dp = (png_byte)((w >> 8) & 0xff); + *(dp + 1) = (png_byte)(w & 0xff); + } +#endif + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 2) + { + png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + if (a == (png_uint_16)0xffff) + { + png_memcpy(dp, sp, 2); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) +#else + else +#endif + { + *dp = (png_byte)((background->gray >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->gray & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else + { + png_uint_16 g, v; + + g = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_composite_16(v, g, a, background_1->gray); + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + } +#endif + } + } + } + break; + } + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 3) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *dp = gamma_table[*sp]; + *(dp + 1) = gamma_table[*(sp + 1)]; + *(dp + 2) = gamma_table[*(sp + 2)]; + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)background->red; + *(dp + 1) = (png_byte)background->green; + *(dp + 2) = (png_byte)background->blue; + } + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, background_1->red); + *dp = gamma_from_1[w]; + v = gamma_to_1[*(sp + 1)]; + png_composite(w, v, a, background_1->green); + *(dp + 1) = gamma_from_1[w]; + v = gamma_to_1[*(sp + 2)]; + png_composite(w, v, a, background_1->blue); + *(dp + 2) = gamma_from_1[w]; + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 3) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *dp = *sp; + *(dp + 1) = *(sp + 1); + *(dp + 2) = *(sp + 2); + } + else if (a == 0) + { + *dp = (png_byte)background->red; + *(dp + 1) = (png_byte)background->green; + *(dp + 2) = (png_byte)background->blue; + } + else + { + png_composite(*dp, *sp, a, background->red); + png_composite(*(dp + 1), *(sp + 1), a, + background->green); + png_composite(*(dp + 2), *(sp + 2), a, + background->blue); + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 8, dp += 6) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(dp + 2) = (png_byte)((v >> 8) & 0xff); + *(dp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(dp + 4) = (png_byte)((v >> 8) & 0xff); + *(dp + 5) = (png_byte)(v & 0xff); + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)((background->red >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->red & 0xff); + *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(dp + 3) = (png_byte)(background->green & 0xff); + *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(dp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v, w, x; + + v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(w, v, a, background_1->red); + x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; + *dp = (png_byte)((x >> 8) & 0xff); + *(dp + 1) = (png_byte)(x & 0xff); + v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)]; + png_composite_16(w, v, a, background_1->green); + x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; + *(dp + 2) = (png_byte)((x >> 8) & 0xff); + *(dp + 3) = (png_byte)(x & 0xff); + v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)]; + png_composite_16(w, v, a, background_1->blue); + x = gamma_16_from_1[(w & 0xff) >> gamma_shift][w >> 8]; + *(dp + 4) = (png_byte)((x >> 8) & 0xff); + *(dp + 5) = (png_byte)(x & 0xff); + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 8, dp += 6) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) + { + png_memcpy(dp, sp, 6); + } + else if (a == 0) + { + *dp = (png_byte)((background->red >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->red & 0xff); + *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(dp + 3) = (png_byte)(background->green & 0xff); + *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(dp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v; + + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + png_composite_16(v, r, a, background->red); + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + png_composite_16(v, g, a, background->green); + *(dp + 2) = (png_byte)((v >> 8) & 0xff); + *(dp + 3) = (png_byte)(v & 0xff); + png_composite_16(v, b, a, background->blue); + *(dp + 4) = (png_byte)((v >> 8) & 0xff); + *(dp + 5) = (png_byte)(v & 0xff); + } + } + } + } + break; + } + } + + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + row_info->channels--; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + } +} +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +/* Gamma correct the image, avoiding the alpha channel. Make sure + * you do this after you deal with the transparency issue on grayscale + * or RGB images. If your bit depth is 8, use gamma_table, if it + * is 16, use gamma_16_table and gamma_shift. Build these with + * build_gamma_table(). + */ +void /* PRIVATE */ +png_do_gamma(png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift) +{ + png_bytep sp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_gamma"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + ((row_info->bit_depth <= 8 && gamma_table != NULL) || + (row_info->bit_depth == 16 && gamma_16_table != NULL))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + sp++; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp += 2; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + case PNG_COLOR_TYPE_GRAY: + { + if (row_info->bit_depth == 2) + { + sp = row; + for (i = 0; i < row_width; i += 4) + { + int a = *sp & 0xc0; + int b = *sp & 0x30; + int c = *sp & 0x0c; + int d = *sp & 0x03; + + *sp = (png_byte)( + ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)]) ) & 0xc0)| + ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)| + ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)| + ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) )); + sp++; + } + } + if (row_info->bit_depth == 4) + { + sp = row; + for (i = 0; i < row_width; i += 2) + { + int msb = *sp & 0xf0; + int lsb = *sp & 0x0f; + + *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0) + | (((int)gamma_table[(lsb << 4) | lsb]) >> 4)); + sp++; + } + } + else if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + } + } + else if (row_info->bit_depth == 16) + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + } + } +} +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expands a palette row to an RGB or RGBA row depending + * upon whether you supply trans and num_trans. + */ +void /* PRIVATE */ +png_do_expand_palette(png_row_infop row_info, png_bytep row, + png_colorp palette, png_bytep trans, int num_trans) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand_palette"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 1; + else + *dp = 0; + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)value; + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((row_width & 0x01) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)value; + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift += 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + switch (row_info->bit_depth) + { + case 8: + { + if (trans != NULL) + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + + for (i = 0; i < row_width; i++) + { + if ((int)(*sp) >= num_trans) + *dp-- = 0xff; + else + *dp-- = trans[*sp]; + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + row_info->color_type = 6; + row_info->channels = 4; + } + else + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width * 3) - 1; + + for (i = 0; i < row_width; i++) + { + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + row_info->color_type = 2; + row_info->channels = 3; + } + break; + } + } + } +} + +/* If the bit depth < 8, it is expanded to 8. Also, if the already + * expanded transparency value is supplied, an alpha channel is built. + */ +void /* PRIVATE */ +png_do_expand(png_row_infop row_info, png_bytep row, + png_color_16p trans_value) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_uint_16 gray = (png_uint_16)(trans_value ? trans_value->gray : 0); + + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + gray = (png_uint_16)((gray&0x01)*0xff); + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 0xff; + else + *dp = 0; + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + gray = (png_uint_16)((gray&0x03)*0x55); + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)(value | (value << 2) | (value << 4) | + (value << 6)); + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + gray = (png_uint_16)((gray&0x0f)*0x11); + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)(value | (value << 4)); + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift = 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + + if (trans_value != NULL) + { + if (row_info->bit_depth == 8) + { + gray = gray & 0xff; + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (*sp == gray) + *dp-- = 0; + else + *dp-- = 0xff; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + png_byte gray_high = (gray >> 8) & 0xff; + png_byte gray_low = gray & 0xff; + sp = row + row_info->rowbytes - 1; + dp = row + (row_info->rowbytes << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 1) == gray_high && *(sp) == gray_low) + { + *dp-- = 0; + *dp-- = 0; + } + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; + row_info->channels = 2; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_width); + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value) + { + if (row_info->bit_depth == 8) + { + png_byte red = trans_value->red & 0xff; + png_byte green = trans_value->green & 0xff; + png_byte blue = trans_value->blue & 0xff; + sp = row + (png_size_t)row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue) + *dp-- = 0; + else + *dp-- = 0xff; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + png_byte red_high = (trans_value->red >> 8) & 0xff; + png_byte green_high = (trans_value->green >> 8) & 0xff; + png_byte blue_high = (trans_value->blue >> 8) & 0xff; + png_byte red_low = trans_value->red & 0xff; + png_byte green_low = trans_value->green & 0xff; + png_byte blue_low = trans_value->blue & 0xff; + sp = row + row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 3) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 5) == red_high && + *(sp - 4) == red_low && + *(sp - 3) == green_high && + *(sp - 2) == green_low && + *(sp - 1) == blue_high && + *(sp ) == blue_low) + { + *dp-- = 0; + *dp-- = 0; + } + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + row_info->channels = 4; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + } +} +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +void /* PRIVATE */ +png_do_dither(png_row_infop row_info, png_bytep row, + png_bytep palette_lookup, png_bytep dither_lookup) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_dither"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB && + palette_lookup && row_info->bit_depth == 8) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + + /* this looks real messy, but the compiler will reduce + it down to a reasonable formula. For example, with + 5 bits per color, we get: + p = (((r >> 3) & 0x1f) << 10) | + (((g >> 3) & 0x1f) << 5) | + ((b >> 3) & 0x1f); + */ + p = (((r >> (8 - PNG_DITHER_RED_BITS)) & + ((1 << PNG_DITHER_RED_BITS) - 1)) << + (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) | + (((g >> (8 - PNG_DITHER_GREEN_BITS)) & + ((1 << PNG_DITHER_GREEN_BITS) - 1)) << + (PNG_DITHER_BLUE_BITS)) | + ((b >> (8 - PNG_DITHER_BLUE_BITS)) & + ((1 << PNG_DITHER_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + palette_lookup != NULL && row_info->bit_depth == 8) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + sp++; + + p = (((r >> (8 - PNG_DITHER_RED_BITS)) & + ((1 << PNG_DITHER_RED_BITS) - 1)) << + (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) | + (((g >> (8 - PNG_DITHER_GREEN_BITS)) & + ((1 << PNG_DITHER_GREEN_BITS) - 1)) << + (PNG_DITHER_BLUE_BITS)) | + ((b >> (8 - PNG_DITHER_BLUE_BITS)) & + ((1 << PNG_DITHER_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && + dither_lookup && row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + *sp = dither_lookup[*sp]; + } + } + } +} +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +#if defined(PNG_READ_GAMMA_SUPPORTED) +static PNG_CONST int png_gamma_shift[] = + {0x10, 0x21, 0x42, 0x84, 0x110, 0x248, 0x550, 0xff0, 0x00}; + +/* We build the 8- or 16-bit gamma tables here. Note that for 16-bit + * tables, we don't make a full table if we are reducing to 8-bit in + * the future. Note also how the gamma_16 tables are segmented so that + * we don't need to allocate > 64K chunks for a full 16-bit table. + */ +void /* PRIVATE */ +png_build_gamma_table(png_structp png_ptr) +{ + png_debug(1, "in png_build_gamma_table"); + + if (png_ptr->bit_depth <= 8) + { + int i; + double g; + + if (png_ptr->screen_gamma > .000001) + g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + else + g = 1.0; + + png_ptr->gamma_table = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_table[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & ((PNG_BACKGROUND) | PNG_RGB_TO_GRAY)) + { + + g = 1.0 / (png_ptr->gamma); + + png_ptr->gamma_to_1 = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_to_1[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + } + + + png_ptr->gamma_from_1 = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + if (png_ptr->screen_gamma > 0.000001) + g = 1.0 / png_ptr->screen_gamma; + else + g = png_ptr->gamma; /* probably doing rgb_to_gray */ + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_from_1[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ + } + else + { + double g; + int i, j, shift, num; + int sig_bit; + png_uint_32 ig; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + sig_bit = (int)png_ptr->sig_bit.red; + if ((int)png_ptr->sig_bit.green > sig_bit) + sig_bit = png_ptr->sig_bit.green; + if ((int)png_ptr->sig_bit.blue > sig_bit) + sig_bit = png_ptr->sig_bit.blue; + } + else + { + sig_bit = (int)png_ptr->sig_bit.gray; + } + + if (sig_bit > 0) + shift = 16 - sig_bit; + else + shift = 0; + + if (png_ptr->transformations & PNG_16_TO_8) + { + if (shift < (16 - PNG_MAX_GAMMA_8)) + shift = (16 - PNG_MAX_GAMMA_8); + } + + if (shift > 8) + shift = 8; + if (shift < 0) + shift = 0; + + png_ptr->gamma_shift = (png_byte)shift; + + num = (1 << (8 - shift)); + + if (png_ptr->screen_gamma > .000001) + g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + else + g = 1.0; + + png_ptr->gamma_16_table = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof(png_uint_16p))); + + if (png_ptr->transformations & (PNG_16_TO_8 | PNG_BACKGROUND)) + { + double fin, fout; + png_uint_32 last, max; + + png_memset(png_ptr->gamma_16_table, 0, num * png_sizeof(png_uint_16p)); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof(png_uint_16))); + } + + g = 1.0 / g; + last = 0; + for (i = 0; i < 256; i++) + { + fout = ((double)i + 0.5) / 256.0; + fin = pow(fout, g); + max = (png_uint_32)(fin * (double)((png_uint_32)num << 8)); + while (last <= max) + { + png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] + [(int)(last >> (8 - shift))] = (png_uint_16)( + (png_uint_16)i | ((png_uint_16)i << 8)); + last++; + } + } + while (last < ((png_uint_32)num << 8)) + { + png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] + [(int)(last >> (8 - shift))] = (png_uint_16)65535L; + last++; + } + } + else + { + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof(png_uint_16))); + + ig = (((png_uint_32)i * (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_table[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY)) + { + + g = 1.0 / (png_ptr->gamma); + + png_ptr->gamma_16_to_1 = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof(png_uint_16p ))); + + png_memset(png_ptr->gamma_16_to_1, 0, num * png_sizeof(png_uint_16p)); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_to_1[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof(png_uint_16))); + + ig = (((png_uint_32)i * + (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_to_1[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + + if (png_ptr->screen_gamma > 0.000001) + g = 1.0 / png_ptr->screen_gamma; + else + g = png_ptr->gamma; /* probably doing rgb_to_gray */ + + png_ptr->gamma_16_from_1 = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof(png_uint_16p))); + + png_memset(png_ptr->gamma_16_from_1, 0, + num * png_sizeof(png_uint_16p)); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_from_1[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof(png_uint_16))); + + ig = (((png_uint_32)i * + (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_from_1[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ + } +} +#endif +/* To do: install integer version of png_build_gamma_table here */ +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +/* undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_read_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_intrapixel"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((256 + *rp + *(rp+1))&0xff); + *(rp+2) = (png_byte)((256 + *(rp+2) + *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1); + png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3); + png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5); + png_uint_32 red = (png_uint_32)((s0 + s1 + 65536L) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2 + s1 + 65536L) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/ext/libpng/pngrutil.c b/ext/libpng/pngrutil.c new file mode 100644 index 00000000..a8793688 --- /dev/null +++ b/ext/libpng/pngrutil.c @@ -0,0 +1,3221 @@ + +/* pngrutil.c - utilities to read a PNG file + * + * Last changed in libpng 1.2.34 [December 18, 2008] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2008 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file contains routines that are only called from within + * libpng itself during the course of reading an image. + */ + +#define PNG_INTERNAL +#include "png.h" +#if defined(PNG_READ_SUPPORTED) + +#if defined(_WIN32_WCE) && (_WIN32_WCE<0x500) +# define WIN32_WCE_OLD +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +# if defined(WIN32_WCE_OLD) +/* strtod() function is not supported on WindowsCE */ +__inline double png_strtod(png_structp png_ptr, PNG_CONST char *nptr, char **endptr) +{ + double result = 0; + int len; + wchar_t *str, *end; + + len = MultiByteToWideChar(CP_ACP, 0, nptr, -1, NULL, 0); + str = (wchar_t *)png_malloc(png_ptr, len * png_sizeof(wchar_t)); + if ( NULL != str ) + { + MultiByteToWideChar(CP_ACP, 0, nptr, -1, str, len); + result = wcstod(str, &end); + len = WideCharToMultiByte(CP_ACP, 0, end, -1, NULL, 0, NULL, NULL); + *endptr = (char *)nptr + (png_strlen(nptr) - len + 1); + png_free(png_ptr, str); + } + return result; +} +# else +# define png_strtod(p,a,b) strtod(a,b) +# endif +#endif + +png_uint_32 PNGAPI +png_get_uint_31(png_structp png_ptr, png_bytep buf) +{ +#ifdef PNG_READ_BIG_ENDIAN_SUPPORTED + png_uint_32 i = png_get_uint_32(buf); +#else + /* Avoid an extra function call by inlining the result. */ + png_uint_32 i = ((png_uint_32)(*buf) << 24) + + ((png_uint_32)(*(buf + 1)) << 16) + + ((png_uint_32)(*(buf + 2)) << 8) + + (png_uint_32)(*(buf + 3)); +#endif + if (i > PNG_UINT_31_MAX) + png_error(png_ptr, "PNG unsigned integer out of range."); + return (i); +} +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */ +png_uint_32 PNGAPI +png_get_uint_32(png_bytep buf) +{ + png_uint_32 i = ((png_uint_32)(*buf) << 24) + + ((png_uint_32)(*(buf + 1)) << 16) + + ((png_uint_32)(*(buf + 2)) << 8) + + (png_uint_32)(*(buf + 3)); + + return (i); +} + +/* Grab a signed 32-bit integer from a buffer in big-endian format. The + * data is stored in the PNG file in two's complement format, and it is + * assumed that the machine format for signed integers is the same. */ +png_int_32 PNGAPI +png_get_int_32(png_bytep buf) +{ + png_int_32 i = ((png_int_32)(*buf) << 24) + + ((png_int_32)(*(buf + 1)) << 16) + + ((png_int_32)(*(buf + 2)) << 8) + + (png_int_32)(*(buf + 3)); + + return (i); +} + +/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */ +png_uint_16 PNGAPI +png_get_uint_16(png_bytep buf) +{ + png_uint_16 i = (png_uint_16)(((png_uint_16)(*buf) << 8) + + (png_uint_16)(*(buf + 1))); + + return (i); +} +#endif /* PNG_READ_BIG_ENDIAN_SUPPORTED */ + +/* Read the chunk header (length + type name). + * Put the type name into png_ptr->chunk_name, and return the length. + */ +png_uint_32 /* PRIVATE */ +png_read_chunk_header(png_structp png_ptr) +{ + png_byte buf[8]; + png_uint_32 length; + + /* read the length and the chunk name */ + png_read_data(png_ptr, buf, 8); + length = png_get_uint_31(png_ptr, buf); + + /* put the chunk name into png_ptr->chunk_name */ + png_memcpy(png_ptr->chunk_name, buf + 4, 4); + + png_debug2(0, "Reading %s chunk, length = %lu", + png_ptr->chunk_name, length); + + /* reset the crc and run it over the chunk name */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, png_ptr->chunk_name, 4); + + /* check to see if chunk name is valid */ + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + + return length; +} + +/* Read data, and (optionally) run it through the CRC. */ +void /* PRIVATE */ +png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length) +{ + if (png_ptr == NULL) return; + png_read_data(png_ptr, buf, length); + png_calculate_crc(png_ptr, buf, length); +} + +/* Optionally skip data and then check the CRC. Depending on whether we + are reading a ancillary or critical chunk, and how the program has set + things up, we may calculate the CRC on the data and print a message. + Returns '1' if there was a CRC error, '0' otherwise. */ +int /* PRIVATE */ +png_crc_finish(png_structp png_ptr, png_uint_32 skip) +{ + png_size_t i; + png_size_t istop = png_ptr->zbuf_size; + + for (i = (png_size_t)skip; i > istop; i -= istop) + { + png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + } + if (i) + { + png_crc_read(png_ptr, png_ptr->zbuf, i); + } + + if (png_crc_error(png_ptr)) + { + if (((png_ptr->chunk_name[0] & 0x20) && /* Ancillary */ + !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) || + (!(png_ptr->chunk_name[0] & 0x20) && /* Critical */ + (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE))) + { + png_chunk_warning(png_ptr, "CRC error"); + } + else + { + png_chunk_error(png_ptr, "CRC error"); + } + return (1); + } + + return (0); +} + +/* Compare the CRC stored in the PNG file with that calculated by libpng from + the data it has read thus far. */ +int /* PRIVATE */ +png_crc_error(png_structp png_ptr) +{ + png_byte crc_bytes[4]; + png_uint_32 crc; + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + png_read_data(png_ptr, crc_bytes, 4); + + if (need_crc) + { + crc = png_get_uint_32(crc_bytes); + return ((int)(crc != png_ptr->crc)); + } + else + return (0); +} + +#if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \ + defined(PNG_READ_iCCP_SUPPORTED) +/* + * Decompress trailing data in a chunk. The assumption is that chunkdata + * points at an allocated area holding the contents of a chunk with a + * trailing compressed part. What we get back is an allocated area + * holding the original prefix part and an uncompressed version of the + * trailing part (the malloc area passed in is freed). + */ +void /* PRIVATE */ +png_decompress_chunk(png_structp png_ptr, int comp_type, + png_size_t chunklength, + png_size_t prefix_size, png_size_t *newlength) +{ + static PNG_CONST char msg[] = "Error decoding compressed text"; + png_charp text; + png_size_t text_size; + + if (comp_type == PNG_COMPRESSION_TYPE_BASE) + { + int ret = Z_OK; + png_ptr->zstream.next_in = (png_bytep)(png_ptr->chunkdata + prefix_size); + png_ptr->zstream.avail_in = (uInt)(chunklength - prefix_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + text_size = 0; + text = NULL; + + while (png_ptr->zstream.avail_in) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + { + if (png_ptr->zstream.msg != NULL) + png_warning(png_ptr, png_ptr->zstream.msg); + else + png_warning(png_ptr, msg); + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + if (text == NULL) + { + text_size = prefix_size + png_sizeof(msg) + 1; + text = (png_charp)png_malloc_warn(png_ptr, text_size); + if (text == NULL) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_error(png_ptr, "Not enough memory to decompress chunk"); + } + png_memcpy(text, png_ptr->chunkdata, prefix_size); + } + + text[text_size - 1] = 0x00; + + /* Copy what we can of the error message into the text chunk */ + text_size = (png_size_t)(chunklength - + (text - png_ptr->chunkdata) - 1); + if (text_size > png_sizeof(msg)) + text_size = png_sizeof(msg); + png_memcpy(text + prefix_size, msg, text_size); + break; + } + if (!png_ptr->zstream.avail_out || ret == Z_STREAM_END) + { + if (text == NULL) + { + text_size = prefix_size + + png_ptr->zbuf_size - png_ptr->zstream.avail_out; + text = (png_charp)png_malloc_warn(png_ptr, text_size + 1); + if (text == NULL) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_error(png_ptr, + "Not enough memory to decompress chunk."); + } + png_memcpy(text + prefix_size, png_ptr->zbuf, + text_size - prefix_size); + png_memcpy(text, png_ptr->chunkdata, prefix_size); + *(text + text_size) = 0x00; + } + else + { + png_charp tmp; + + tmp = text; + text = (png_charp)png_malloc_warn(png_ptr, + (png_uint_32)(text_size + + png_ptr->zbuf_size - png_ptr->zstream.avail_out + 1)); + if (text == NULL) + { + png_free(png_ptr, tmp); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_error(png_ptr, + "Not enough memory to decompress chunk.."); + } + png_memcpy(text, tmp, text_size); + png_free(png_ptr, tmp); + png_memcpy(text + text_size, png_ptr->zbuf, + (png_ptr->zbuf_size - png_ptr->zstream.avail_out)); + text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; + *(text + text_size) = 0x00; + } + if (ret == Z_STREAM_END) + break; + else + { + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + } + if (ret != Z_STREAM_END) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char umsg[52]; + + if (ret == Z_BUF_ERROR) + png_snprintf(umsg, 52, + "Buffer error in compressed datastream in %s chunk", + png_ptr->chunk_name); + else if (ret == Z_DATA_ERROR) + png_snprintf(umsg, 52, + "Data error in compressed datastream in %s chunk", + png_ptr->chunk_name); + else + png_snprintf(umsg, 52, + "Incomplete compressed datastream in %s chunk", + png_ptr->chunk_name); + png_warning(png_ptr, umsg); +#else + png_warning(png_ptr, + "Incomplete compressed datastream in chunk other than IDAT"); +#endif + text_size = prefix_size; + if (text == NULL) + { + text = (png_charp)png_malloc_warn(png_ptr, text_size+1); + if (text == NULL) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_error(png_ptr, "Not enough memory for text."); + } + png_memcpy(text, png_ptr->chunkdata, prefix_size); + } + *(text + text_size) = 0x00; + } + + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = text; + *newlength=text_size; + } + else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */ + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char umsg[50]; + + png_snprintf(umsg, 50, "Unknown zTXt compression type %d", comp_type); + png_warning(png_ptr, umsg); +#else + png_warning(png_ptr, "Unknown zTXt compression type"); +#endif + + *(png_ptr->chunkdata + prefix_size) = 0x00; + *newlength = prefix_size; + } +} +#endif + +/* read and check the IDHR chunk */ +void /* PRIVATE */ +png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[13]; + png_uint_32 width, height; + int bit_depth, color_type, compression_type, filter_type; + int interlace_type; + + png_debug(1, "in png_handle_IHDR"); + + if (png_ptr->mode & PNG_HAVE_IHDR) + png_error(png_ptr, "Out of place IHDR"); + + /* check the length */ + if (length != 13) + png_error(png_ptr, "Invalid IHDR chunk"); + + png_ptr->mode |= PNG_HAVE_IHDR; + + png_crc_read(png_ptr, buf, 13); + png_crc_finish(png_ptr, 0); + + width = png_get_uint_31(png_ptr, buf); + height = png_get_uint_31(png_ptr, buf + 4); + bit_depth = buf[8]; + color_type = buf[9]; + compression_type = buf[10]; + filter_type = buf[11]; + interlace_type = buf[12]; + + /* set internal variables */ + png_ptr->width = width; + png_ptr->height = height; + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->interlaced = (png_byte)interlace_type; + png_ptr->color_type = (png_byte)color_type; +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + + /* find number of channels */ + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_PALETTE: + png_ptr->channels = 1; + break; + case PNG_COLOR_TYPE_RGB: + png_ptr->channels = 3; + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + png_ptr->channels = 2; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + png_ptr->channels = 4; + break; + } + + /* set up other useful info */ + png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * + png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width); + png_debug1(3, "bit_depth = %d", png_ptr->bit_depth); + png_debug1(3, "channels = %d", png_ptr->channels); + png_debug1(3, "rowbytes = %lu", png_ptr->rowbytes); + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, + color_type, interlace_type, compression_type, filter_type); +} + +/* read and check the palette */ +void /* PRIVATE */ +png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_color palette[PNG_MAX_PALETTE_LENGTH]; + int num, i; +#ifndef PNG_NO_POINTER_INDEXING + png_colorp pal_ptr; +#endif + + png_debug(1, "in png_handle_PLTE"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before PLTE"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid PLTE after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + png_error(png_ptr, "Duplicate PLTE chunk"); + + png_ptr->mode |= PNG_HAVE_PLTE; + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring PLTE chunk in grayscale PNG"); + png_crc_finish(png_ptr, length); + return; + } +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_crc_finish(png_ptr, length); + return; + } +#endif + + if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) + { + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_warning(png_ptr, "Invalid palette chunk"); + png_crc_finish(png_ptr, length); + return; + } + else + { + png_error(png_ptr, "Invalid palette chunk"); + } + } + + num = (int)length / 3; + +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + pal_ptr->red = buf[0]; + pal_ptr->green = buf[1]; + pal_ptr->blue = buf[2]; + } +#else + for (i = 0; i < num; i++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + /* don't depend upon png_color being any order */ + palette[i].red = buf[0]; + palette[i].green = buf[1]; + palette[i].blue = buf[2]; + } +#endif + + /* If we actually NEED the PLTE chunk (ie for a paletted image), we do + whatever the normal CRC configuration tells us. However, if we + have an RGB image, the PLTE can be considered ancillary, so + we will act as though it is. */ +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#endif + { + png_crc_finish(png_ptr, 0); + } +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + else if (png_crc_error(png_ptr)) /* Only if we have a CRC error */ + { + /* If we don't want to use the data from an ancillary chunk, + we have two options: an error abort, or a warning and we + ignore the data in this chunk (which should be OK, since + it's considered ancillary for a RGB or RGBA image). */ + if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE)) + { + if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) + { + png_chunk_error(png_ptr, "CRC error"); + } + else + { + png_chunk_warning(png_ptr, "CRC error"); + return; + } + } + /* Otherwise, we (optionally) emit a warning and use the chunk. */ + else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) + { + png_chunk_warning(png_ptr, "CRC error"); + } + } +#endif + + png_set_PLTE(png_ptr, info_ptr, palette, num); + +#if defined(PNG_READ_tRNS_SUPPORTED) + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + if (png_ptr->num_trans > (png_uint_16)num) + { + png_warning(png_ptr, "Truncating incorrect tRNS chunk length"); + png_ptr->num_trans = (png_uint_16)num; + } + if (info_ptr->num_trans > (png_uint_16)num) + { + png_warning(png_ptr, "Truncating incorrect info tRNS chunk length"); + info_ptr->num_trans = (png_uint_16)num; + } + } + } +#endif + +} + +void /* PRIVATE */ +png_handle_IEND(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_debug(1, "in png_handle_IEND"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT)) + { + png_error(png_ptr, "No image in file"); + } + + png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); + + if (length != 0) + { + png_warning(png_ptr, "Incorrect IEND chunk length"); + } + png_crc_finish(png_ptr, length); + + info_ptr = info_ptr; /* quiet compiler warnings about unused info_ptr */ +} + +#if defined(PNG_READ_gAMA_SUPPORTED) +void /* PRIVATE */ +png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_fixed_point igamma; +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif + png_byte buf[4]; + + png_debug(1, "in png_handle_gAMA"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before gAMA"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid gAMA after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place gAMA chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) +#if defined(PNG_READ_sRGB_SUPPORTED) + && !(info_ptr->valid & PNG_INFO_sRGB) +#endif + ) + { + png_warning(png_ptr, "Duplicate gAMA chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 4) + { + png_warning(png_ptr, "Incorrect gAMA chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 4); + if (png_crc_finish(png_ptr, 0)) + return; + + igamma = (png_fixed_point)png_get_uint_32(buf); + /* check for zero gamma */ + if (igamma == 0) + { + png_warning(png_ptr, + "Ignoring gAMA chunk with gamma=0"); + return; + } + +#if defined(PNG_READ_sRGB_SUPPORTED) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) + if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + { + png_warning(png_ptr, + "Ignoring incorrect gAMA value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO + fprintf(stderr, "gamma = (%d/100000)", (int)igamma); +#endif + return; + } +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float)igamma / (float)100000.0; +# ifdef PNG_READ_GAMMA_SUPPORTED + png_ptr->gamma = file_gamma; +# endif + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_gAMA_fixed(png_ptr, info_ptr, igamma); +#endif +} +#endif + +#if defined(PNG_READ_sBIT_SUPPORTED) +void /* PRIVATE */ +png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_size_t truelen; + png_byte buf[4]; + + png_debug(1, "in png_handle_sBIT"); + + buf[0] = buf[1] = buf[2] = buf[3] = 0; + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sBIT"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sBIT after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + { + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place sBIT chunk"); + } + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)) + { + png_warning(png_ptr, "Duplicate sBIT chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 3; + else + truelen = (png_size_t)png_ptr->channels; + + if (length != truelen || length > 4) + { + png_warning(png_ptr, "Incorrect sBIT chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) + return; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[1]; + png_ptr->sig_bit.blue = buf[2]; + png_ptr->sig_bit.alpha = buf[3]; + } + else + { + png_ptr->sig_bit.gray = buf[0]; + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[0]; + png_ptr->sig_bit.blue = buf[0]; + png_ptr->sig_bit.alpha = buf[1]; + } + png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); +} +#endif + +#if defined(PNG_READ_cHRM_SUPPORTED) +void /* PRIVATE */ +png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[32]; +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif + png_fixed_point int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, + int_y_green, int_x_blue, int_y_blue; + + png_uint_32 uint_x, uint_y; + + png_debug(1, "in png_handle_cHRM"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before cHRM"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid cHRM after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Missing PLTE before cHRM"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM) +#if defined(PNG_READ_sRGB_SUPPORTED) + && !(info_ptr->valid & PNG_INFO_sRGB) +#endif + ) + { + png_warning(png_ptr, "Duplicate cHRM chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 32) + { + png_warning(png_ptr, "Incorrect cHRM chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 32); + if (png_crc_finish(png_ptr, 0)) + return; + + uint_x = png_get_uint_32(buf); + uint_y = png_get_uint_32(buf + 4); + int_x_white = (png_fixed_point)uint_x; + int_y_white = (png_fixed_point)uint_y; + + uint_x = png_get_uint_32(buf + 8); + uint_y = png_get_uint_32(buf + 12); + int_x_red = (png_fixed_point)uint_x; + int_y_red = (png_fixed_point)uint_y; + + uint_x = png_get_uint_32(buf + 16); + uint_y = png_get_uint_32(buf + 20); + int_x_green = (png_fixed_point)uint_x; + int_y_green = (png_fixed_point)uint_y; + + uint_x = png_get_uint_32(buf + 24); + uint_y = png_get_uint_32(buf + 28); + int_x_blue = (png_fixed_point)uint_x; + int_y_blue = (png_fixed_point)uint_y; + +#ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float)int_x_white / (float)100000.0; + white_y = (float)int_y_white / (float)100000.0; + red_x = (float)int_x_red / (float)100000.0; + red_y = (float)int_y_red / (float)100000.0; + green_x = (float)int_x_green / (float)100000.0; + green_y = (float)int_y_green / (float)100000.0; + blue_x = (float)int_x_blue / (float)100000.0; + blue_y = (float)int_y_blue / (float)100000.0; +#endif + +#if defined(PNG_READ_sRGB_SUPPORTED) + if ((info_ptr != NULL) && (info_ptr->valid & PNG_INFO_sRGB)) + { + if (PNG_OUT_OF_RANGE(int_x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(int_y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(int_x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(int_y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(int_x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(int_y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(int_x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(int_y_blue, 6000, 1000)) + { + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO +#ifdef PNG_FLOATING_POINT_SUPPORTED + fprintf(stderr, "wx=%f, wy=%f, rx=%f, ry=%f\n", + white_x, white_y, red_x, red_y); + fprintf(stderr, "gx=%f, gy=%f, bx=%f, by=%f\n", + green_x, green_y, blue_x, blue_y); +#else + fprintf(stderr, "wx=%ld, wy=%ld, rx=%ld, ry=%ld\n", + int_x_white, int_y_white, int_x_red, int_y_red); + fprintf(stderr, "gx=%ld, gy=%ld, bx=%ld, by=%ld\n", + int_x_green, int_y_green, int_x_blue, int_y_blue); +#endif +#endif /* PNG_NO_CONSOLE_IO */ + } + return; + } +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_cHRM_fixed(png_ptr, info_ptr, + int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, + int_y_green, int_x_blue, int_y_blue); +#endif +} +#endif + +#if defined(PNG_READ_sRGB_SUPPORTED) +void /* PRIVATE */ +png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + int intent; + png_byte buf[1]; + + png_debug(1, "in png_handle_sRGB"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sRGB"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sRGB after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place sRGB chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) + { + png_warning(png_ptr, "Duplicate sRGB chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 1) + { + png_warning(png_ptr, "Incorrect sRGB chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 1); + if (png_crc_finish(png_ptr, 0)) + return; + + intent = buf[0]; + /* check for bad intent */ + if (intent >= PNG_sRGB_INTENT_LAST) + { + png_warning(png_ptr, "Unknown sRGB intent"); + return; + } + +#if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)) + { + png_fixed_point igamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + igamma=info_ptr->int_gamma; +#else +# ifdef PNG_FLOATING_POINT_SUPPORTED + igamma=(png_fixed_point)(info_ptr->gamma * 100000.); +# endif +#endif + if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + { + png_warning(png_ptr, + "Ignoring incorrect gAMA value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO +# ifdef PNG_FIXED_POINT_SUPPORTED + fprintf(stderr, "incorrect gamma=(%d/100000)\n", + (int)png_ptr->int_gamma); +# else +# ifdef PNG_FLOATING_POINT_SUPPORTED + fprintf(stderr, "incorrect gamma=%f\n", png_ptr->gamma); +# endif +# endif +#endif + } + } +#endif /* PNG_READ_gAMA_SUPPORTED */ + +#ifdef PNG_READ_cHRM_SUPPORTED +#ifdef PNG_FIXED_POINT_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + if (PNG_OUT_OF_RANGE(info_ptr->int_x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_blue, 6000, 1000)) + { + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); + } +#endif /* PNG_FIXED_POINT_SUPPORTED */ +#endif /* PNG_READ_cHRM_SUPPORTED */ + + png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent); +} +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#if defined(PNG_READ_iCCP_SUPPORTED) +void /* PRIVATE */ +png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_byte compression_type; + png_bytep pC; + png_charp profile; + png_uint_32 skip = 0; + png_uint_32 profile_size, profile_length; + png_size_t slength, prefix_length, data_length; + + png_debug(1, "in png_handle_iCCP"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before iCCP"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid iCCP after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place iCCP chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP)) + { + png_warning(png_ptr, "Duplicate iCCP chunk"); + png_crc_finish(png_ptr, length); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "iCCP chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc(png_ptr, length + 1); + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; + + for (profile = png_ptr->chunkdata; *profile; profile++) + /* empty loop to find end of name */ ; + + ++profile; + + /* there should be at least one zero (the compression type byte) + following the separator, and we should be on it */ + if ( profile >= png_ptr->chunkdata + slength - 1) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "Malformed iCCP chunk"); + return; + } + + /* compression_type should always be zero */ + compression_type = *profile++; + if (compression_type) + { + png_warning(png_ptr, "Ignoring nonzero compression type in iCCP chunk"); + compression_type = 0x00; /* Reset it to zero (libpng-1.0.6 through 1.0.8 + wrote nonzero) */ + } + + prefix_length = profile - png_ptr->chunkdata; + png_decompress_chunk(png_ptr, compression_type, + slength, prefix_length, &data_length); + + profile_length = data_length - prefix_length; + + if ( prefix_length > data_length || profile_length < 4) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "Profile size field missing from iCCP chunk"); + return; + } + + /* Check the profile_size recorded in the first 32 bits of the ICC profile */ + pC = (png_bytep)(png_ptr->chunkdata + prefix_length); + profile_size = ((*(pC ))<<24) | + ((*(pC + 1))<<16) | + ((*(pC + 2))<< 8) | + ((*(pC + 3)) ); + + if (profile_size < profile_length) + profile_length = profile_size; + + if (profile_size > profile_length) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "Ignoring truncated iCCP profile."); + return; + } + + png_set_iCCP(png_ptr, info_ptr, png_ptr->chunkdata, + compression_type, png_ptr->chunkdata + prefix_length, profile_length); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; +} +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#if defined(PNG_READ_sPLT_SUPPORTED) +void /* PRIVATE */ +png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_bytep entry_start; + png_sPLT_t new_palette; +#ifdef PNG_NO_POINTER_INDEXING + png_sPLT_entryp pp; +#endif + int data_length, entry_size, i; + png_uint_32 skip = 0; + png_size_t slength; + + png_debug(1, "in png_handle_sPLT"); + + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sPLT"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sPLT after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "sPLT chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc(png_ptr, length + 1); + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; + + for (entry_start = (png_bytep)png_ptr->chunkdata; *entry_start; entry_start++) + /* empty loop to find end of name */ ; + ++entry_start; + + /* a sample depth should follow the separator, and we should be on it */ + if (entry_start > (png_bytep)png_ptr->chunkdata + slength - 2) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "malformed sPLT chunk"); + return; + } + + new_palette.depth = *entry_start++; + entry_size = (new_palette.depth == 8 ? 6 : 10); + data_length = (slength - (entry_start - (png_bytep)png_ptr->chunkdata)); + + /* integrity-check the data length */ + if (data_length % entry_size) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "sPLT chunk has bad length"); + return; + } + + new_palette.nentries = (png_int_32) ( data_length / entry_size); + if ((png_uint_32) new_palette.nentries > + (png_uint_32) (PNG_SIZE_MAX / png_sizeof(png_sPLT_entry))) + { + png_warning(png_ptr, "sPLT chunk too long"); + return; + } + new_palette.entries = (png_sPLT_entryp)png_malloc_warn( + png_ptr, new_palette.nentries * png_sizeof(png_sPLT_entry)); + if (new_palette.entries == NULL) + { + png_warning(png_ptr, "sPLT chunk requires too much memory"); + return; + } + +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0; i < new_palette.nentries; i++) + { + png_sPLT_entryp pp = new_palette.entries + i; + + if (new_palette.depth == 8) + { + pp->red = *entry_start++; + pp->green = *entry_start++; + pp->blue = *entry_start++; + pp->alpha = *entry_start++; + } + else + { + pp->red = png_get_uint_16(entry_start); entry_start += 2; + pp->green = png_get_uint_16(entry_start); entry_start += 2; + pp->blue = png_get_uint_16(entry_start); entry_start += 2; + pp->alpha = png_get_uint_16(entry_start); entry_start += 2; + } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#else + pp = new_palette.entries; + for (i = 0; i < new_palette.nentries; i++) + { + + if (new_palette.depth == 8) + { + pp[i].red = *entry_start++; + pp[i].green = *entry_start++; + pp[i].blue = *entry_start++; + pp[i].alpha = *entry_start++; + } + else + { + pp[i].red = png_get_uint_16(entry_start); entry_start += 2; + pp[i].green = png_get_uint_16(entry_start); entry_start += 2; + pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; + pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; + } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#endif + + /* discard all chunk data except the name and stash that */ + new_palette.name = png_ptr->chunkdata; + + png_set_sPLT(png_ptr, info_ptr, &new_palette, 1); + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_free(png_ptr, new_palette.entries); +} +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#if defined(PNG_READ_tRNS_SUPPORTED) +void /* PRIVATE */ +png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_tRNS"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before tRNS"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid tRNS after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_warning(png_ptr, "Duplicate tRNS chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + png_byte buf[2]; + + if (length != 2) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 2); + png_ptr->num_trans = 1; + png_ptr->trans_values.gray = png_get_uint_16(buf); + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_byte buf[6]; + + if (length != 6) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + png_crc_read(png_ptr, buf, (png_size_t)length); + png_ptr->num_trans = 1; + png_ptr->trans_values.red = png_get_uint_16(buf); + png_ptr->trans_values.green = png_get_uint_16(buf + 2); + png_ptr->trans_values.blue = png_get_uint_16(buf + 4); + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + /* Should be an error, but we can cope with it. */ + png_warning(png_ptr, "Missing PLTE before tRNS"); + } + if (length > (png_uint_32)png_ptr->num_palette || + length > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + if (length == 0) + { + png_warning(png_ptr, "Zero length tRNS chunk"); + png_crc_finish(png_ptr, length); + return; + } + png_crc_read(png_ptr, readbuf, (png_size_t)length); + png_ptr->num_trans = (png_uint_16)length; + } + else + { + png_warning(png_ptr, "tRNS chunk not allowed with alpha channel"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_crc_finish(png_ptr, 0)) + { + png_ptr->num_trans = 0; + return; + } + + png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, + &(png_ptr->trans_values)); +} +#endif + +#if defined(PNG_READ_bKGD_SUPPORTED) +void /* PRIVATE */ +png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_size_t truelen; + png_byte buf[6]; + + png_debug(1, "in png_handle_bKGD"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before bKGD"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid bKGD after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_warning(png_ptr, "Missing PLTE before bKGD"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) + { + png_warning(png_ptr, "Duplicate bKGD chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 1; + else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + truelen = 6; + else + truelen = 2; + + if (length != truelen) + { + png_warning(png_ptr, "Incorrect bKGD chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) + return; + + /* We convert the index value into RGB components so that we can allow + * arbitrary RGB values for background when we have transparency, and + * so it is easy to determine the RGB values of the background color + * from the info_ptr struct. */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->background.index = buf[0]; + if (info_ptr && info_ptr->num_palette) + { + if (buf[0] >= info_ptr->num_palette) + { + png_warning(png_ptr, "Incorrect bKGD chunk index value"); + return; + } + png_ptr->background.red = + (png_uint_16)png_ptr->palette[buf[0]].red; + png_ptr->background.green = + (png_uint_16)png_ptr->palette[buf[0]].green; + png_ptr->background.blue = + (png_uint_16)png_ptr->palette[buf[0]].blue; + } + } + else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */ + { + png_ptr->background.red = + png_ptr->background.green = + png_ptr->background.blue = + png_ptr->background.gray = png_get_uint_16(buf); + } + else + { + png_ptr->background.red = png_get_uint_16(buf); + png_ptr->background.green = png_get_uint_16(buf + 2); + png_ptr->background.blue = png_get_uint_16(buf + 4); + } + + png_set_bKGD(png_ptr, info_ptr, &(png_ptr->background)); +} +#endif + +#if defined(PNG_READ_hIST_SUPPORTED) +void /* PRIVATE */ +png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + unsigned int num, i; + png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_hIST"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before hIST"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid hIST after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_warning(png_ptr, "Missing PLTE before hIST"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) + { + png_warning(png_ptr, "Duplicate hIST chunk"); + png_crc_finish(png_ptr, length); + return; + } + + num = length / 2 ; + if (num != (unsigned int) png_ptr->num_palette || num > + (unsigned int) PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, "Incorrect hIST chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + for (i = 0; i < num; i++) + { + png_byte buf[2]; + + png_crc_read(png_ptr, buf, 2); + readbuf[i] = png_get_uint_16(buf); + } + + if (png_crc_finish(png_ptr, 0)) + return; + + png_set_hIST(png_ptr, info_ptr, readbuf); +} +#endif + +#if defined(PNG_READ_pHYs_SUPPORTED) +void /* PRIVATE */ +png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_uint_32 res_x, res_y; + int unit_type; + + png_debug(1, "in png_handle_pHYs"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before pHYs"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid pHYs after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_warning(png_ptr, "Duplicate pHYs chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 9) + { + png_warning(png_ptr, "Incorrect pHYs chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) + return; + + res_x = png_get_uint_32(buf); + res_y = png_get_uint_32(buf + 4); + unit_type = buf[8]; + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type); +} +#endif + +#if defined(PNG_READ_oFFs_SUPPORTED) +void /* PRIVATE */ +png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_int_32 offset_x, offset_y; + int unit_type; + + png_debug(1, "in png_handle_oFFs"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before oFFs"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid oFFs after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + { + png_warning(png_ptr, "Duplicate oFFs chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 9) + { + png_warning(png_ptr, "Incorrect oFFs chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) + return; + + offset_x = png_get_int_32(buf); + offset_y = png_get_int_32(buf + 4); + unit_type = buf[8]; + png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type); +} +#endif + +#if defined(PNG_READ_pCAL_SUPPORTED) +/* read the pCAL chunk (described in the PNG Extensions document) */ +void /* PRIVATE */ +png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_int_32 X0, X1; + png_byte type, nparams; + png_charp buf, units, endptr; + png_charpp params; + png_size_t slength; + int i; + + png_debug(1, "in png_handle_pCAL"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before pCAL"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid pCAL after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) + { + png_warning(png_ptr, "Duplicate pCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + + png_debug1(2, "Allocating and reading pCAL chunk data (%lu bytes)", + length + 1); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "No memory for pCAL purpose."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; /* null terminate the last string */ + + png_debug(3, "Finding end of pCAL purpose string"); + for (buf = png_ptr->chunkdata; *buf; buf++) + /* empty loop */ ; + + endptr = png_ptr->chunkdata + slength; + + /* We need to have at least 12 bytes after the purpose string + in order to get the parameter information. */ + if (endptr <= buf + 12) + { + png_warning(png_ptr, "Invalid pCAL data"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_debug(3, "Reading pCAL X0, X1, type, nparams, and units"); + X0 = png_get_int_32((png_bytep)buf+1); + X1 = png_get_int_32((png_bytep)buf+5); + type = buf[9]; + nparams = buf[10]; + units = buf + 11; + + png_debug(3, "Checking pCAL equation type and number of parameters"); + /* Check that we have the right number of parameters for known + equation types. */ + if ((type == PNG_EQUATION_LINEAR && nparams != 2) || + (type == PNG_EQUATION_BASE_E && nparams != 3) || + (type == PNG_EQUATION_ARBITRARY && nparams != 3) || + (type == PNG_EQUATION_HYPERBOLIC && nparams != 4)) + { + png_warning(png_ptr, "Invalid pCAL parameters for equation type"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + else if (type >= PNG_EQUATION_LAST) + { + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + } + + for (buf = units; *buf; buf++) + /* Empty loop to move past the units string. */ ; + + png_debug(3, "Allocating pCAL parameters array"); + params = (png_charpp)png_malloc_warn(png_ptr, + (png_uint_32)(nparams * png_sizeof(png_charp))) ; + if (params == NULL) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "No memory for pCAL params."); + return; + } + + /* Get pointers to the start of each parameter string. */ + for (i = 0; i < (int)nparams; i++) + { + buf++; /* Skip the null string terminator from previous parameter. */ + + png_debug1(3, "Reading pCAL parameter %d", i); + for (params[i] = buf; buf <= endptr && *buf != 0x00; buf++) + /* Empty loop to move past each parameter string */ ; + + /* Make sure we haven't run out of data yet */ + if (buf > endptr) + { + png_warning(png_ptr, "Invalid pCAL data"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_free(png_ptr, params); + return; + } + } + + png_set_pCAL(png_ptr, info_ptr, png_ptr->chunkdata, X0, X1, type, nparams, + units, params); + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_free(png_ptr, params); +} +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) +/* read the sCAL chunk */ +void /* PRIVATE */ +png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_charp ep; +#ifdef PNG_FLOATING_POINT_SUPPORTED + double width, height; + png_charp vp; +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_charp swidth, sheight; +#endif +#endif + png_size_t slength; + + png_debug(1, "in png_handle_sCAL"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sCAL"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sCAL after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) + { + png_warning(png_ptr, "Duplicate sCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + + png_debug1(2, "Allocating and reading sCAL chunk data (%lu bytes)", + length + 1); + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk"); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; /* null terminate the last string */ + + ep = png_ptr->chunkdata + 1; /* skip unit byte */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + width = png_strtod(png_ptr, ep, &vp); + if (*vp) + { + png_warning(png_ptr, "malformed width string in sCAL chunk"); + return; + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + swidth = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); + if (swidth == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk width"); + return; + } + png_memcpy(swidth, ep, (png_size_t)png_strlen(ep)); +#endif +#endif + + for (ep = png_ptr->chunkdata; *ep; ep++) + /* empty loop */ ; + ep++; + + if (png_ptr->chunkdata + slength < ep) + { + png_warning(png_ptr, "Truncated sCAL chunk"); +#if defined(PNG_FIXED_POINT_SUPPORTED) && \ + !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); +#endif + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + +#ifdef PNG_FLOATING_POINT_SUPPORTED + height = png_strtod(png_ptr, ep, &vp); + if (*vp) + { + png_warning(png_ptr, "malformed height string in sCAL chunk"); + return; + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + sheight = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); + if (sheight == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk height"); + return; + } + png_memcpy(sheight, ep, (png_size_t)png_strlen(ep)); +#endif +#endif + + if (png_ptr->chunkdata + slength < ep +#ifdef PNG_FLOATING_POINT_SUPPORTED + || width <= 0. || height <= 0. +#endif + ) + { + png_warning(png_ptr, "Invalid sCAL data"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); + png_free(png_ptr, sheight); +#endif + return; + } + + +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_sCAL(png_ptr, info_ptr, png_ptr->chunkdata[0], width, height); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_sCAL_s(png_ptr, info_ptr, png_ptr->chunkdata[0], swidth, sheight); +#endif +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); + png_free(png_ptr, sheight); +#endif +} +#endif + +#if defined(PNG_READ_tIME_SUPPORTED) +void /* PRIVATE */ +png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[7]; + png_time mod_time; + + png_debug(1, "in png_handle_tIME"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Out of place tIME chunk"); + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) + { + png_warning(png_ptr, "Duplicate tIME chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + + if (length != 7) + { + png_warning(png_ptr, "Incorrect tIME chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 7); + if (png_crc_finish(png_ptr, 0)) + return; + + mod_time.second = buf[6]; + mod_time.minute = buf[5]; + mod_time.hour = buf[4]; + mod_time.day = buf[3]; + mod_time.month = buf[2]; + mod_time.year = png_get_uint_16(buf); + + png_set_tIME(png_ptr, info_ptr, &mod_time); +} +#endif + +#if defined(PNG_READ_tEXt_SUPPORTED) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp key; + png_charp text; + png_uint_32 skip = 0; + png_size_t slength; + int ret; + + png_debug(1, "in png_handle_tEXt"); + + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before tEXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "tEXt chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "No memory to process text chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + key = png_ptr->chunkdata; + + key[slength] = 0x00; + + for (text = key; *text; text++) + /* empty loop to find end of key */ ; + + if (text != key + slength) + text++; + + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr, "Not enough memory to process text chunk."); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; + text_ptr->itxt_length = 0; +#endif + text_ptr->text = text; + text_ptr->text_length = png_strlen(text); + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_free(png_ptr, text_ptr); + if (ret) + png_warning(png_ptr, "Insufficient memory to process text chunk."); +} +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +/* note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp text; + int comp_type; + int ret; + png_size_t slength, prefix_len, data_len; + + png_debug(1, "in png_handle_zTXt"); + + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before zTXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + /* We will no doubt have problems with chunks even half this size, but + there is no hard and fast rule to tell us where to stop. */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "zTXt chunk too large to fit in memory"); + png_crc_finish(png_ptr, length); + return; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "Out of memory processing zTXt chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; + + for (text = png_ptr->chunkdata; *text; text++) + /* empty loop */ ; + + /* zTXt must have some text after the chunkdataword */ + if (text >= png_ptr->chunkdata + slength - 2) + { + png_warning(png_ptr, "Truncated zTXt chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + else + { + comp_type = *(++text); + if (comp_type != PNG_TEXT_COMPRESSION_zTXt) + { + png_warning(png_ptr, "Unknown compression type in zTXt chunk"); + comp_type = PNG_TEXT_COMPRESSION_zTXt; + } + text++; /* skip the compression_method byte */ + } + prefix_len = text - png_ptr->chunkdata; + + png_decompress_chunk(png_ptr, comp_type, + (png_size_t)length, prefix_len, &data_len); + + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr, "Not enough memory to process zTXt chunk."); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + text_ptr->compression = comp_type; + text_ptr->key = png_ptr->chunkdata; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; + text_ptr->itxt_length = 0; +#endif + text_ptr->text = png_ptr->chunkdata + prefix_len; + text_ptr->text_length = data_len; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, text_ptr); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + if (ret) + png_error(png_ptr, "Insufficient memory to store zTXt chunk."); +} +#endif + +#if defined(PNG_READ_iTXt_SUPPORTED) +/* note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp key, lang, text, lang_key; + int comp_flag; + int comp_type = 0; + int ret; + png_size_t slength, prefix_len, data_len; + + png_debug(1, "in png_handle_iTXt"); + + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before iTXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + /* We will no doubt have problems with chunks even half this size, but + there is no hard and fast rule to tell us where to stop. */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "iTXt chunk too large to fit in memory"); + png_crc_finish(png_ptr, length); + return; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "No memory to process iTXt chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; + + for (lang = png_ptr->chunkdata; *lang; lang++) + /* empty loop */ ; + lang++; /* skip NUL separator */ + + /* iTXt must have a language tag (possibly empty), two compression bytes, + translated keyword (possibly empty), and possibly some text after the + keyword */ + + if (lang >= png_ptr->chunkdata + slength - 3) + { + png_warning(png_ptr, "Truncated iTXt chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + else + { + comp_flag = *lang++; + comp_type = *lang++; + } + + for (lang_key = lang; *lang_key; lang_key++) + /* empty loop */ ; + lang_key++; /* skip NUL separator */ + + if (lang_key >= png_ptr->chunkdata + slength) + { + png_warning(png_ptr, "Truncated iTXt chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + for (text = lang_key; *text; text++) + /* empty loop */ ; + text++; /* skip NUL separator */ + if (text >= png_ptr->chunkdata + slength) + { + png_warning(png_ptr, "Malformed iTXt chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + prefix_len = text - png_ptr->chunkdata; + + key=png_ptr->chunkdata; + if (comp_flag) + png_decompress_chunk(png_ptr, comp_type, + (size_t)length, prefix_len, &data_len); + else + data_len = png_strlen(png_ptr->chunkdata + prefix_len); + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr, "Not enough memory to process iTXt chunk."); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + text_ptr->compression = (int)comp_flag + 1; + text_ptr->lang_key = png_ptr->chunkdata + (lang_key - key); + text_ptr->lang = png_ptr->chunkdata + (lang - key); + text_ptr->itxt_length = data_len; + text_ptr->text_length = 0; + text_ptr->key = png_ptr->chunkdata; + text_ptr->text = png_ptr->chunkdata + prefix_len; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, text_ptr); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + if (ret) + png_error(png_ptr, "Insufficient memory to store iTXt chunk."); +} +#endif + +/* This function is called when we haven't found a handler for a + chunk. If there isn't a problem with the chunk itself (ie bad + chunk name, CRC, or a critical chunk), the chunk is silently ignored + -- unless the PNG_FLAG_UNKNOWN_CHUNKS_SUPPORTED flag is on in which + case it will be saved away to be written out later. */ +void /* PRIVATE */ +png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_uint_32 skip = 0; + + png_debug(1, "in png_handle_unknown"); + + + if (png_ptr->mode & PNG_HAVE_IDAT) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IDAT; +#endif + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) /* not an IDAT */ + png_ptr->mode |= PNG_AFTER_IDAT; + } + + if (!(png_ptr->chunk_name[0] & 0x20)) + { +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + && png_ptr->read_user_chunk_fn == NULL +#endif + ) +#endif + png_chunk_error(png_ptr, "unknown critical chunk"); + } + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if ((png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) || + (png_ptr->read_user_chunk_fn != NULL)) + { +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "unknown chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + png_memcpy((png_charp)png_ptr->unknown_chunk.name, + (png_charp)png_ptr->chunk_name, + png_sizeof(png_ptr->unknown_chunk.name)); + png_ptr->unknown_chunk.name[png_sizeof(png_ptr->unknown_chunk.name)-1] = '\0'; + png_ptr->unknown_chunk.size = (png_size_t)length; + if (length == 0) + png_ptr->unknown_chunk.data = NULL; + else + { + png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length); + png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length); + } +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + if (png_ptr->read_user_chunk_fn != NULL) + { + /* callback to user unknown chunk handler */ + int ret; + ret = (*(png_ptr->read_user_chunk_fn)) + (png_ptr, &png_ptr->unknown_chunk); + if (ret < 0) + png_chunk_error(png_ptr, "error in user chunk"); + if (ret == 0) + { + if (!(png_ptr->chunk_name[0] & 0x20)) + if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS) + png_chunk_error(png_ptr, "unknown critical chunk"); + png_set_unknown_chunks(png_ptr, info_ptr, + &png_ptr->unknown_chunk, 1); + } + } + else +#endif + png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } + else +#endif + skip = length; + + png_crc_finish(png_ptr, skip); + +#if !defined(PNG_READ_USER_CHUNKS_SUPPORTED) + info_ptr = info_ptr; /* quiet compiler warnings about unused info_ptr */ +#endif +} + +/* This function is called to verify that a chunk name is valid. + This function can't have the "critical chunk check" incorporated + into it, since in the future we will need to be able to call user + functions to handle unknown critical chunks after we check that + the chunk name itself is valid. */ + +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) + +void /* PRIVATE */ +png_check_chunk_name(png_structp png_ptr, png_bytep chunk_name) +{ + png_debug(1, "in png_check_chunk_name"); + if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) || + isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3])) + { + png_chunk_error(png_ptr, "invalid chunk type"); + } +} + +/* Combines the row recently read in with the existing pixels in the + row. This routine takes care of alpha and transparency if requested. + This routine also handles the two methods of progressive display + of interlaced images, depending on the mask value. + The mask value describes which pixels are to be combined with + the row. The pattern always repeats every 8 pixels, so just 8 + bits are needed. A one indicates the pixel is to be combined, + a zero indicates the pixel is to be skipped. This is in addition + to any alpha or transparency value associated with the pixel. If + you want all pixels to be combined, pass 0xff (255) in mask. */ + +void /* PRIVATE */ +png_combine_row(png_structp png_ptr, png_bytep row, int mask) +{ + png_debug(1, "in png_combine_row"); + if (mask == 0xff) + { + png_memcpy(row, png_ptr->row_buf + 1, + PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->width)); + } + else + { + switch (png_ptr->row_info.pixel_depth) + { + case 1: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_inc, s_start, s_end; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 7; + s_inc = 1; + } + else +#endif + { + s_start = 7; + s_end = 0; + s_inc = -1; + } + + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + int value; + + value = (*sp >> shift) & 0x01; + *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + case 2: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_start, s_end, s_inc; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + int value; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 6; + s_inc = 2; + } + else +#endif + { + s_start = 6; + s_end = 0; + s_inc = -2; + } + + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0x03; + *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + case 4: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_start, s_end, s_inc; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + int value; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 4; + s_inc = 4; + } + else +#endif + { + s_start = 4; + s_end = 0; + s_inc = -4; + } + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0xf; + *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + default: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + png_size_t pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + png_byte m = 0x80; + + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + png_memcpy(dp, sp, pixel_bytes); + } + + sp += pixel_bytes; + dp += pixel_bytes; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + } + } +} + +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* OLD pre-1.0.9 interface: +void png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, + png_uint_32 transformations) + */ +void /* PRIVATE */ +png_do_read_interlace(png_structp png_ptr) +{ + png_row_infop row_info = &(png_ptr->row_info); + png_bytep row = png_ptr->row_buf + 1; + int pass = png_ptr->pass; + png_uint_32 transformations = png_ptr->transformations; +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + /* offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +#endif + + png_debug(1, "in png_do_read_interlace"); + if (row != NULL && row_info != NULL) + { + png_uint_32 final_width; + + final_width = row_info->width * png_pass_inc[pass]; + + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 3); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_byte v; + png_uint_32 i; + int j; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)((row_info->width + 7) & 0x07); + dshift = (int)((final_width + 7) & 0x07); + s_start = 7; + s_end = 0; + s_inc = -1; + } + else +#endif + { + sshift = 7 - (int)((row_info->width + 7) & 0x07); + dshift = 7 - (int)((final_width + 7) & 0x07); + s_start = 0; + s_end = 7; + s_inc = 1; + } + + for (i = 0; i < row_info->width; i++) + { + v = (png_byte)((*sp >> sshift) & 0x01); + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + case 2: + { + png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2); + png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_uint_32 i; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 3) & 0x03) << 1); + dshift = (int)(((final_width + 3) & 0x03) << 1); + s_start = 6; + s_end = 0; + s_inc = -2; + } + else +#endif + { + sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1); + dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1); + s_start = 0; + s_end = 6; + s_inc = 2; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0x03); + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + case 4: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 1); + int sshift, dshift; + int s_start, s_end, s_inc; + png_uint_32 i; + int jstop = png_pass_inc[pass]; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 1) & 0x01) << 2); + dshift = (int)(((final_width + 1) & 0x01) << 2); + s_start = 4; + s_end = 0; + s_inc = -4; + } + else +#endif + { + sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2); + dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2); + s_start = 0; + s_end = 4; + s_inc = 4; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v = (png_byte)((*sp >> sshift) & 0xf); + int j; + + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + default: + { + png_size_t pixel_bytes = (row_info->pixel_depth >> 3); + png_bytep sp = row + (png_size_t)(row_info->width - 1) * pixel_bytes; + png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; + + int jstop = png_pass_inc[pass]; + png_uint_32 i; + + for (i = 0; i < row_info->width; i++) + { + png_byte v[8]; + int j; + + png_memcpy(v, sp, pixel_bytes); + for (j = 0; j < jstop; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sp -= pixel_bytes; + } + break; + } + } + row_info->width = final_width; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, final_width); + } +#if !defined(PNG_READ_PACKSWAP_SUPPORTED) + transformations = transformations; /* silence compiler warning */ +#endif +} +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + +void /* PRIVATE */ +png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep row, + png_bytep prev_row, int filter) +{ + png_debug(1, "in png_read_filter_row"); + png_debug2(2, "row = %lu, filter = %d", png_ptr->row_number, filter); + switch (filter) + { + case PNG_FILTER_VALUE_NONE: + break; + case PNG_FILTER_VALUE_SUB: + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp = row + bpp; + png_bytep lp = row; + + for (i = bpp; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_UP: + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_bytep rp = row; + png_bytep pp = prev_row; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_AVG: + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop = row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) / 2 )) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *lp++) / 2 ) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_PAETH: + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_bytep cp = prev_row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop=row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) /* use leftover rp,pp */ + { + int a, b, c, pa, pb, pc, p; + + a = *lp++; + b = *pp++; + c = *cp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + /* + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; + */ + + p = (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c; + + *rp = (png_byte)(((int)(*rp) + p) & 0xff); + rp++; + } + break; + } + default: + png_warning(png_ptr, "Ignoring bad adaptive filter type"); + *row = 0; + break; + } +} + +void /* PRIVATE */ +png_read_finish_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif /* PNG_READ_INTERLACING_SUPPORTED */ +#endif + + png_debug(1, "in png_read_finish_row"); + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + png_memset_check(png_ptr, png_ptr->prev_row, 0, + png_ptr->rowbytes + 1); + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (!(png_ptr->num_rows)) + continue; + } + else /* if (png_ptr->transformations & PNG_INTERLACE) */ + break; + } while (png_ptr->iwidth == 0); + + if (png_ptr->pass < 7) + return; + } +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IDAT; +#endif + char extra; + int ret; + + png_ptr->zstream.next_out = (Byte *)&extra; + png_ptr->zstream.avail_out = (uInt)1; + for (;;) + { + if (!(png_ptr->zstream.avail_in)) + { + while (!png_ptr->idat_size) + { + png_byte chunk_length[4]; + + png_crc_finish(png_ptr, 0); + + png_read_data(png_ptr, chunk_length, 4); + png_ptr->idat_size = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_error(png_ptr, "Not enough image data"); + + } + png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_in = png_ptr->zbuf; + if (png_ptr->zbuf_size > png_ptr->idat_size) + png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in); + png_ptr->idat_size -= png_ptr->zstream.avail_in; + } + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret == Z_STREAM_END) + { + if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in || + png_ptr->idat_size) + png_warning(png_ptr, "Extra compressed data"); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + if (ret != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : + "Decompression Error"); + + if (!(png_ptr->zstream.avail_out)) + { + png_warning(png_ptr, "Extra compressed data."); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + + } + png_ptr->zstream.avail_out = 0; + } + + if (png_ptr->idat_size || png_ptr->zstream.avail_in) + png_warning(png_ptr, "Extra compression data"); + + inflateReset(&png_ptr->zstream); + + png_ptr->mode |= PNG_AFTER_IDAT; +} + +void /* PRIVATE */ +png_read_start_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif +#endif + + int max_pixel_depth; + png_size_t row_bytes; + + png_debug(1, "in png_read_start_row"); + png_ptr->zstream.avail_in = 0; + png_init_read_transformations(png_ptr); +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + else + png_ptr->num_rows = png_ptr->height; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + png_ptr->irowbytes = + PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1; + } + else +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + { + png_ptr->num_rows = png_ptr->height; + png_ptr->iwidth = png_ptr->width; + png_ptr->irowbytes = png_ptr->rowbytes + 1; + } + max_pixel_depth = png_ptr->pixel_depth; + +#if defined(PNG_READ_PACK_SUPPORTED) + if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8) + max_pixel_depth = 8; +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans) + max_pixel_depth = 32; + else + max_pixel_depth = 24; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth < 8) + max_pixel_depth = 8; + if (png_ptr->num_trans) + max_pixel_depth *= 2; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (png_ptr->num_trans) + { + max_pixel_depth *= 4; + max_pixel_depth /= 3; + } + } + } +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & (PNG_FILLER)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + max_pixel_depth = 32; + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth <= 8) + max_pixel_depth = 16; + else + max_pixel_depth = 32; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (max_pixel_depth <= 32) + max_pixel_depth = 32; + else + max_pixel_depth = 64; + } + } +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + { + if ( +#if defined(PNG_READ_EXPAND_SUPPORTED) + (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) || +#endif +#if defined(PNG_READ_FILLER_SUPPORTED) + (png_ptr->transformations & (PNG_FILLER)) || +#endif + png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (max_pixel_depth <= 16) + max_pixel_depth = 32; + else + max_pixel_depth = 64; + } + else + { + if (max_pixel_depth <= 8) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 32; + else + max_pixel_depth = 24; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 64; + else + max_pixel_depth = 48; + } + } +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \ +defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + int user_pixel_depth = png_ptr->user_transform_depth* + png_ptr->user_transform_channels; + if (user_pixel_depth > max_pixel_depth) + max_pixel_depth=user_pixel_depth; + } +#endif + + /* align the width on the next larger 8 pixels. Mainly used + for interlacing */ + row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7)); + /* calculate the maximum bytes needed, adding a byte and a pixel + for safety's sake */ + row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) + + 1 + ((max_pixel_depth + 7) >> 3); +#ifdef PNG_MAX_MALLOC_64K + if (row_bytes > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + + if (row_bytes + 64 > png_ptr->old_big_row_buf_size) + { + png_free(png_ptr, png_ptr->big_row_buf); + png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes+64); + png_ptr->row_buf = png_ptr->big_row_buf+32; + png_ptr->old_big_row_buf_size = row_bytes+64; + } + +#ifdef PNG_MAX_MALLOC_64K + if ((png_uint_32)png_ptr->rowbytes + 1 > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + if ((png_uint_32)png_ptr->rowbytes > (png_uint_32)(PNG_SIZE_MAX - 1)) + png_error(png_ptr, "Row has too many bytes to allocate in memory."); + + if (png_ptr->rowbytes+1 > png_ptr->old_prev_row_size) + { + png_free(png_ptr, png_ptr->prev_row); + png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)( + png_ptr->rowbytes + 1)); + png_ptr->old_prev_row_size = png_ptr->rowbytes+1; + } + + png_memset_check(png_ptr, png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + + png_debug1(3, "width = %lu,", png_ptr->width); + png_debug1(3, "height = %lu,", png_ptr->height); + png_debug1(3, "iwidth = %lu,", png_ptr->iwidth); + png_debug1(3, "num_rows = %lu,", png_ptr->num_rows); + png_debug1(3, "rowbytes = %lu,", png_ptr->rowbytes); + png_debug1(3, "irowbytes = %lu", png_ptr->irowbytes); + + png_ptr->flags |= PNG_FLAG_ROW_INIT; +} +#endif /* PNG_READ_SUPPORTED */ diff --git a/ext/libpng/pngset.c b/ext/libpng/pngset.c new file mode 100644 index 00000000..bdc04581 --- /dev/null +++ b/ext/libpng/pngset.c @@ -0,0 +1,1255 @@ + +/* pngset.c - storage of image information into info struct + * + * Last changed in libpng 1.2.35 [February 14, 2009] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2009 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * The functions here are used during reads to store data from the file + * into the info struct, and during writes to store application data + * into the info struct for writing into the file. This abstracts the + * info struct and allows us to change the structure in the future. + */ + +#define PNG_INTERNAL +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +#if defined(PNG_bKGD_SUPPORTED) +void PNGAPI +png_set_bKGD(png_structp png_ptr, png_infop info_ptr, png_color_16p background) +{ + png_debug1(1, "in %s storage function", "bKGD"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->background), background, png_sizeof(png_color_16)); + info_ptr->valid |= PNG_INFO_bKGD; +} +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_cHRM(png_structp png_ptr, png_infop info_ptr, + double white_x, double white_y, double red_x, double red_y, + double green_x, double green_y, double blue_x, double blue_y) +{ + png_debug1(1, "in %s storage function", "cHRM"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_white = (float)white_x; + info_ptr->y_white = (float)white_y; + info_ptr->x_red = (float)red_x; + info_ptr->y_red = (float)red_y; + info_ptr->x_green = (float)green_x; + info_ptr->y_green = (float)green_y; + info_ptr->x_blue = (float)blue_x; + info_ptr->y_blue = (float)blue_y; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_x_white = (png_fixed_point)(white_x*100000.+0.5); + info_ptr->int_y_white = (png_fixed_point)(white_y*100000.+0.5); + info_ptr->int_x_red = (png_fixed_point)( red_x*100000.+0.5); + info_ptr->int_y_red = (png_fixed_point)( red_y*100000.+0.5); + info_ptr->int_x_green = (png_fixed_point)(green_x*100000.+0.5); + info_ptr->int_y_green = (png_fixed_point)(green_y*100000.+0.5); + info_ptr->int_x_blue = (png_fixed_point)( blue_x*100000.+0.5); + info_ptr->int_y_blue = (png_fixed_point)( blue_y*100000.+0.5); +#endif + info_ptr->valid |= PNG_INFO_cHRM; +} +#endif /* PNG_FLOATING_POINT_SUPPORTED */ + +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, + png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y) +{ + png_debug1(1, "in %s storage function", "cHRM fixed"); + if (png_ptr == NULL || info_ptr == NULL) + return; + +#if !defined(PNG_NO_CHECK_cHRM) + if (png_check_cHRM_fixed(png_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y)) +#endif + { + info_ptr->int_x_white = white_x; + info_ptr->int_y_white = white_y; + info_ptr->int_x_red = red_x; + info_ptr->int_y_red = red_y; + info_ptr->int_x_green = green_x; + info_ptr->int_y_green = green_y; + info_ptr->int_x_blue = blue_x; + info_ptr->int_y_blue = blue_y; +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->x_white = (float)(white_x/100000.); + info_ptr->y_white = (float)(white_y/100000.); + info_ptr->x_red = (float)( red_x/100000.); + info_ptr->y_red = (float)( red_y/100000.); + info_ptr->x_green = (float)(green_x/100000.); + info_ptr->y_green = (float)(green_y/100000.); + info_ptr->x_blue = (float)( blue_x/100000.); + info_ptr->y_blue = (float)( blue_y/100000.); +#endif + info_ptr->valid |= PNG_INFO_cHRM; + } +} +#endif /* PNG_FIXED_POINT_SUPPORTED */ +#endif /* PNG_cHRM_SUPPORTED */ + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma) +{ + double gamma; + png_debug1(1, "in %s storage function", "gAMA"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Check for overflow */ + if (file_gamma > 21474.83) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + gamma=21474.83; + } + else + gamma = file_gamma; + info_ptr->gamma = (float)gamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = (int)(gamma*100000.+.5); +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if (gamma == 0.0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif +void PNGAPI +png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point + int_gamma) +{ + png_fixed_point gamma; + + png_debug1(1, "in %s storage function", "gAMA"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (int_gamma > (png_fixed_point) PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + gamma=PNG_UINT_31_MAX; + } + else + { + if (int_gamma < 0) + { + png_warning(png_ptr, "Setting negative gamma to zero"); + gamma = 0; + } + else + gamma = int_gamma; + } +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = (float)(gamma/100000.); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = gamma; +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if (gamma == 0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +void PNGAPI +png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist) +{ + int i; + + png_debug1(1, "in %s storage function", "hIST"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if (info_ptr->num_palette == 0 || info_ptr->num_palette + > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, + "Invalid palette size, hIST allocation skipped."); + return; + } + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); +#endif + /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in version + 1.2.1 */ + png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr, + (png_uint_32)(PNG_MAX_PALETTE_LENGTH * png_sizeof(png_uint_16))); + if (png_ptr->hist == NULL) + { + png_warning(png_ptr, "Insufficient memory for hIST chunk data."); + return; + } + + for (i = 0; i < info_ptr->num_palette; i++) + png_ptr->hist[i] = hist[i]; + info_ptr->hist = png_ptr->hist; + info_ptr->valid |= PNG_INFO_hIST; + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_HIST; +#else + png_ptr->flags |= PNG_FLAG_FREE_HIST; +#endif +} +#endif + +void PNGAPI +png_set_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + png_debug1(1, "in %s storage function", "IHDR"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* check for width and height valid values */ + if (width == 0 || height == 0) + png_error(png_ptr, "Image width or height is zero in IHDR"); +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (width > png_ptr->user_width_max || height > png_ptr->user_height_max) + png_error(png_ptr, "image size exceeds user limits in IHDR"); +#else + if (width > PNG_USER_WIDTH_MAX || height > PNG_USER_HEIGHT_MAX) + png_error(png_ptr, "image size exceeds user limits in IHDR"); +#endif + if (width > PNG_UINT_31_MAX || height > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image size in IHDR"); + if ( width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + png_warning(png_ptr, "Width is too large for libpng to process pixels"); + + /* check other values */ + if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && + bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth in IHDR"); + + if (color_type < 0 || color_type == 1 || + color_type == 5 || color_type > 6) + png_error(png_ptr, "Invalid color type in IHDR"); + + if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) || + ((color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8)) + png_error(png_ptr, "Invalid color type/bit depth combination in IHDR"); + + if (interlace_type >= PNG_INTERLACE_LAST) + png_error(png_ptr, "Unknown interlace method in IHDR"); + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_error(png_ptr, "Unknown compression method in IHDR"); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + /* Accept filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not read a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&png_ptr->mng_features_permitted) + png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); + if (filter_type != PNG_FILTER_TYPE_BASE) + { + if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA))) + png_error(png_ptr, "Unknown filter method in IHDR"); + if (png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) + png_warning(png_ptr, "Invalid filter method in IHDR"); + } +#else + if (filter_type != PNG_FILTER_TYPE_BASE) + png_error(png_ptr, "Unknown filter method in IHDR"); +#endif + + info_ptr->width = width; + info_ptr->height = height; + info_ptr->bit_depth = (png_byte)bit_depth; + info_ptr->color_type =(png_byte) color_type; + info_ptr->compression_type = (png_byte)compression_type; + info_ptr->filter_type = (png_byte)filter_type; + info_ptr->interlace_type = (png_byte)interlace_type; + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); + + /* check for potential overflow */ + if (width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + info_ptr->rowbytes = (png_size_t)0; + else + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); +} + +#if defined(PNG_oFFs_SUPPORTED) +void PNGAPI +png_set_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 offset_x, png_int_32 offset_y, int unit_type) +{ + png_debug1(1, "in %s storage function", "oFFs"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_offset = offset_x; + info_ptr->y_offset = offset_y; + info_ptr->offset_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_oFFs; +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +void PNGAPI +png_set_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params) +{ + png_uint_32 length; + int i; + + png_debug1(1, "in %s storage function", "pCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + length = png_strlen(purpose) + 1; + png_debug1(3, "allocating purpose for info (%lu bytes)", + (unsigned long)length); + info_ptr->pcal_purpose = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_purpose == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL purpose."); + return; + } + png_memcpy(info_ptr->pcal_purpose, purpose, (png_size_t)length); + + png_debug(3, "storing X0, X1, type, and nparams in info"); + info_ptr->pcal_X0 = X0; + info_ptr->pcal_X1 = X1; + info_ptr->pcal_type = (png_byte)type; + info_ptr->pcal_nparams = (png_byte)nparams; + + length = png_strlen(units) + 1; + png_debug1(3, "allocating units for info (%lu bytes)", + (unsigned long)length); + info_ptr->pcal_units = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_units == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL units."); + return; + } + png_memcpy(info_ptr->pcal_units, units, (png_size_t)length); + + info_ptr->pcal_params = (png_charpp)png_malloc_warn(png_ptr, + (png_uint_32)((nparams + 1) * png_sizeof(png_charp))); + if (info_ptr->pcal_params == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL params."); + return; + } + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_PCAL; +#endif + + png_memset(info_ptr->pcal_params, 0, (nparams + 1) * png_sizeof(png_charp)); + + for (i = 0; i < nparams; i++) + { + length = png_strlen(params[i]) + 1; + png_debug2(3, "allocating parameter %d for info (%lu bytes)", i, + (unsigned long)length); + info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_params[i] == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL parameter."); + return; + } + png_memcpy(info_ptr->pcal_params[i], params[i], (png_size_t)length); + } + + info_ptr->valid |= PNG_INFO_pCAL; +} +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) || defined(PNG_WRITE_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_sCAL(png_structp png_ptr, png_infop info_ptr, + int unit, double width, double height) +{ + png_debug1(1, "in %s storage function", "sCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + info_ptr->scal_pixel_width = width; + info_ptr->scal_pixel_height = height; + + info_ptr->valid |= PNG_INFO_sCAL; +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int unit, png_charp swidth, png_charp sheight) +{ + png_uint_32 length; + + png_debug1(1, "in %s storage function", "sCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + + length = png_strlen(swidth) + 1; + png_debug1(3, "allocating unit for info (%u bytes)", + (unsigned int)length); + info_ptr->scal_s_width = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_width == NULL) + { + png_warning(png_ptr, + "Memory allocation failed while processing sCAL."); + return; + } + png_memcpy(info_ptr->scal_s_width, swidth, (png_size_t)length); + + length = png_strlen(sheight) + 1; + png_debug1(3, "allocating unit for info (%u bytes)", + (unsigned int)length); + info_ptr->scal_s_height = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_height == NULL) + { + png_free (png_ptr, info_ptr->scal_s_width); + info_ptr->scal_s_width = NULL; + png_warning(png_ptr, + "Memory allocation failed while processing sCAL."); + return; + } + png_memcpy(info_ptr->scal_s_height, sheight, (png_size_t)length); + info_ptr->valid |= PNG_INFO_sCAL; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_SCAL; +#endif +} +#endif +#endif +#endif + +#if defined(PNG_pHYs_SUPPORTED) +void PNGAPI +png_set_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 res_x, png_uint_32 res_y, int unit_type) +{ + png_debug1(1, "in %s storage function", "pHYs"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_pixels_per_unit = res_x; + info_ptr->y_pixels_per_unit = res_y; + info_ptr->phys_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_pHYs; +} +#endif + +void PNGAPI +png_set_PLTE(png_structp png_ptr, png_infop info_ptr, + png_colorp palette, int num_palette) +{ + + png_debug1(1, "in %s storage function", "PLTE"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Invalid palette length"); + else + { + png_warning(png_ptr, "Invalid palette length"); + return; + } + } + + /* + * It may not actually be necessary to set png_ptr->palette here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); +#endif + + /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead + of num_palette entries, + in case of an invalid PNG file that has too-large sample values. */ + png_ptr->palette = (png_colorp)png_malloc(png_ptr, + PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color)); + png_memset(png_ptr->palette, 0, PNG_MAX_PALETTE_LENGTH * + png_sizeof(png_color)); + png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof(png_color)); + info_ptr->palette = png_ptr->palette; + info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_PLTE; +#else + png_ptr->flags |= PNG_FLAG_FREE_PLTE; +#endif + + info_ptr->valid |= PNG_INFO_PLTE; +} + +#if defined(PNG_sBIT_SUPPORTED) +void PNGAPI +png_set_sBIT(png_structp png_ptr, png_infop info_ptr, + png_color_8p sig_bit) +{ + png_debug1(1, "in %s storage function", "sBIT"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->sig_bit), sig_bit, png_sizeof(png_color_8)); + info_ptr->valid |= PNG_INFO_sBIT; +} +#endif + +#if defined(PNG_sRGB_SUPPORTED) +void PNGAPI +png_set_sRGB(png_structp png_ptr, png_infop info_ptr, int intent) +{ + png_debug1(1, "in %s storage function", "sRGB"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->srgb_intent = (png_byte)intent; + info_ptr->valid |= PNG_INFO_sRGB; +} + +void PNGAPI +png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr, + int intent) +{ +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_fixed_point int_file_gamma; +#endif +#endif +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif + png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, + int_green_y, int_blue_x, int_blue_y; +#endif + png_debug1(1, "in %s storage function", "sRGB_gAMA_and_cHRM"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_set_sRGB(png_ptr, info_ptr, intent); + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float).45455; + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + int_file_gamma = 45455L; + png_set_gAMA_fixed(png_ptr, info_ptr, int_file_gamma); +#endif +#endif + +#if defined(PNG_cHRM_SUPPORTED) + int_white_x = 31270L; + int_white_y = 32900L; + int_red_x = 64000L; + int_red_y = 33000L; + int_green_x = 30000L; + int_green_y = 60000L; + int_blue_x = 15000L; + int_blue_y = 6000L; + +#ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float).3127; + white_y = (float).3290; + red_x = (float).64; + red_y = (float).33; + green_x = (float).30; + green_y = (float).60; + blue_x = (float).15; + blue_y = (float).06; +#endif + +#if !defined(PNG_NO_CHECK_cHRM) + if (png_check_cHRM_fixed(png_ptr, + int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, + int_green_y, int_blue_x, int_blue_y)) +#endif + { +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_cHRM_fixed(png_ptr, info_ptr, + int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, + int_green_y, int_blue_x, int_blue_y); +#endif +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +#endif + } +#endif /* cHRM */ +} +#endif /* sRGB */ + + +#if defined(PNG_iCCP_SUPPORTED) +void PNGAPI +png_set_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen) +{ + png_charp new_iccp_name; + png_charp new_iccp_profile; + png_uint_32 length; + + png_debug1(1, "in %s storage function", "iCCP"); + if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) + return; + + length = png_strlen(name)+1; + new_iccp_name = (png_charp)png_malloc_warn(png_ptr, length); + if (new_iccp_name == NULL) + { + png_warning(png_ptr, "Insufficient memory to process iCCP chunk."); + return; + } + png_memcpy(new_iccp_name, name, length); + new_iccp_profile = (png_charp)png_malloc_warn(png_ptr, proflen); + if (new_iccp_profile == NULL) + { + png_free (png_ptr, new_iccp_name); + png_warning(png_ptr, + "Insufficient memory to process iCCP profile."); + return; + } + png_memcpy(new_iccp_profile, profile, (png_size_t)proflen); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); + + info_ptr->iccp_proflen = proflen; + info_ptr->iccp_name = new_iccp_name; + info_ptr->iccp_profile = new_iccp_profile; + /* Compression is always zero but is here so the API and info structure + * does not have to change if we introduce multiple compression types */ + info_ptr->iccp_compression = (png_byte)compression_type; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_ICCP; +#endif + info_ptr->valid |= PNG_INFO_iCCP; +} +#endif + +#if defined(PNG_TEXT_SUPPORTED) +void PNGAPI +png_set_text(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int ret; + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, num_text); + if (ret) + png_error(png_ptr, "Insufficient memory to store text"); +} + +int /* PRIVATE */ +png_set_text_2(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int i; + + png_debug1(1, "in %s storage function", (png_ptr->chunk_name[0] == '\0' ? + "text" : (png_const_charp)png_ptr->chunk_name)); + + if (png_ptr == NULL || info_ptr == NULL || num_text == 0) + return(0); + + /* Make sure we have enough space in the "text" array in info_struct + * to hold all of the incoming text_ptr objects. + */ + if (info_ptr->num_text + num_text > info_ptr->max_text) + { + if (info_ptr->text != NULL) + { + png_textp old_text; + int old_max; + + old_max = info_ptr->max_text; + info_ptr->max_text = info_ptr->num_text + num_text + 8; + old_text = info_ptr->text; + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)(info_ptr->max_text * png_sizeof(png_text))); + if (info_ptr->text == NULL) + { + png_free(png_ptr, old_text); + return(1); + } + png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max * + png_sizeof(png_text))); + png_free(png_ptr, old_text); + } + else + { + info_ptr->max_text = num_text + 8; + info_ptr->num_text = 0; + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)(info_ptr->max_text * png_sizeof(png_text))); + if (info_ptr->text == NULL) + return(1); +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_TEXT; +#endif + } + png_debug1(3, "allocated %d entries for info_ptr->text", + info_ptr->max_text); + } + for (i = 0; i < num_text; i++) + { + png_size_t text_length, key_len; + png_size_t lang_len, lang_key_len; + png_textp textp = &(info_ptr->text[info_ptr->num_text]); + + if (text_ptr[i].key == NULL) + continue; + + key_len = png_strlen(text_ptr[i].key); + + if (text_ptr[i].compression <= 0) + { + lang_len = 0; + lang_key_len = 0; + } + else +#ifdef PNG_iTXt_SUPPORTED + { + /* set iTXt data */ + if (text_ptr[i].lang != NULL) + lang_len = png_strlen(text_ptr[i].lang); + else + lang_len = 0; + if (text_ptr[i].lang_key != NULL) + lang_key_len = png_strlen(text_ptr[i].lang_key); + else + lang_key_len = 0; + } +#else + { + png_warning(png_ptr, "iTXt chunk not supported."); + continue; + } +#endif + + if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0') + { + text_length = 0; +#ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) + textp->compression = PNG_ITXT_COMPRESSION_NONE; + else +#endif + textp->compression = PNG_TEXT_COMPRESSION_NONE; + } + else + { + text_length = png_strlen(text_ptr[i].text); + textp->compression = text_ptr[i].compression; + } + + textp->key = (png_charp)png_malloc_warn(png_ptr, + (png_uint_32) + (key_len + text_length + lang_len + lang_key_len + 4)); + if (textp->key == NULL) + return(1); + png_debug2(2, "Allocated %lu bytes at %x in png_set_text", + (png_uint_32) + (key_len + lang_len + lang_key_len + text_length + 4), + (int)textp->key); + + png_memcpy(textp->key, text_ptr[i].key, + (png_size_t)(key_len)); + *(textp->key + key_len) = '\0'; +#ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) + { + textp->lang = textp->key + key_len + 1; + png_memcpy(textp->lang, text_ptr[i].lang, lang_len); + *(textp->lang + lang_len) = '\0'; + textp->lang_key = textp->lang + lang_len + 1; + png_memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len); + *(textp->lang_key + lang_key_len) = '\0'; + textp->text = textp->lang_key + lang_key_len + 1; + } + else +#endif + { +#ifdef PNG_iTXt_SUPPORTED + textp->lang=NULL; + textp->lang_key=NULL; +#endif + textp->text = textp->key + key_len + 1; + } + if (text_length) + png_memcpy(textp->text, text_ptr[i].text, + (png_size_t)(text_length)); + *(textp->text + text_length) = '\0'; + +#ifdef PNG_iTXt_SUPPORTED + if (textp->compression > 0) + { + textp->text_length = 0; + textp->itxt_length = text_length; + } + else +#endif + { + textp->text_length = text_length; +#ifdef PNG_iTXt_SUPPORTED + textp->itxt_length = 0; +#endif + } + info_ptr->num_text++; + png_debug1(3, "transferred text chunk %d", info_ptr->num_text); + } + return(0); +} +#endif + +#if defined(PNG_tIME_SUPPORTED) +void PNGAPI +png_set_tIME(png_structp png_ptr, png_infop info_ptr, png_timep mod_time) +{ + png_debug1(1, "in %s storage function", "tIME"); + if (png_ptr == NULL || info_ptr == NULL || + (png_ptr->mode & PNG_WROTE_tIME)) + return; + + png_memcpy(&(info_ptr->mod_time), mod_time, png_sizeof(png_time)); + info_ptr->valid |= PNG_INFO_tIME; +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +void PNGAPI +png_set_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep trans, int num_trans, png_color_16p trans_values) +{ + png_debug1(1, "in %s storage function", "tRNS"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (trans != NULL) + { + /* + * It may not actually be necessary to set png_ptr->trans here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); +#endif + + /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ + png_ptr->trans = info_ptr->trans = (png_bytep)png_malloc(png_ptr, + (png_uint_32)PNG_MAX_PALETTE_LENGTH); + if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) + png_memcpy(info_ptr->trans, trans, (png_size_t)num_trans); + } + + if (trans_values != NULL) + { + int sample_max = (1 << info_ptr->bit_depth); + if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY && + (int)trans_values->gray > sample_max) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB && + ((int)trans_values->red > sample_max || + (int)trans_values->green > sample_max || + (int)trans_values->blue > sample_max))) + png_warning(png_ptr, + "tRNS chunk has out-of-range samples for bit_depth"); + png_memcpy(&(info_ptr->trans_values), trans_values, + png_sizeof(png_color_16)); + if (num_trans == 0) + num_trans = 1; + } + + info_ptr->num_trans = (png_uint_16)num_trans; + if (num_trans != 0) + { + info_ptr->valid |= PNG_INFO_tRNS; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_TRNS; +#else + png_ptr->flags |= PNG_FLAG_FREE_TRNS; +#endif + } +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +void PNGAPI +png_set_sPLT(png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries) +/* + * entries - array of png_sPLT_t structures + * to be added to the list of palettes + * in the info structure. + * nentries - number of palette structures to be + * added. + */ +{ + png_sPLT_tp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL) + return; + + np = (png_sPLT_tp)png_malloc_warn(png_ptr, + (info_ptr->splt_palettes_num + nentries) * + (png_uint_32)png_sizeof(png_sPLT_t)); + if (np == NULL) + { + png_warning(png_ptr, "No memory for sPLT palettes."); + return; + } + + png_memcpy(np, info_ptr->splt_palettes, + info_ptr->splt_palettes_num * png_sizeof(png_sPLT_t)); + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes=NULL; + + for (i = 0; i < nentries; i++) + { + png_sPLT_tp to = np + info_ptr->splt_palettes_num + i; + png_sPLT_tp from = entries + i; + png_uint_32 length; + + length = png_strlen(from->name) + 1; + to->name = (png_charp)png_malloc_warn(png_ptr, length); + if (to->name == NULL) + { + png_warning(png_ptr, + "Out of memory while processing sPLT chunk"); + continue; + } + png_memcpy(to->name, from->name, length); + to->entries = (png_sPLT_entryp)png_malloc_warn(png_ptr, + (png_uint_32)(from->nentries * png_sizeof(png_sPLT_entry))); + if (to->entries == NULL) + { + png_warning(png_ptr, + "Out of memory while processing sPLT chunk"); + png_free(png_ptr, to->name); + to->name = NULL; + continue; + } + png_memcpy(to->entries, from->entries, + from->nentries * png_sizeof(png_sPLT_entry)); + to->nentries = from->nentries; + to->depth = from->depth; + } + + info_ptr->splt_palettes = np; + info_ptr->splt_palettes_num += nentries; + info_ptr->valid |= PNG_INFO_sPLT; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_SPLT; +#endif +} +#endif /* PNG_sPLT_SUPPORTED */ + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +void PNGAPI +png_set_unknown_chunks(png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns) +{ + png_unknown_chunkp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL || num_unknowns == 0) + return; + + np = (png_unknown_chunkp)png_malloc_warn(png_ptr, + (png_uint_32)((info_ptr->unknown_chunks_num + num_unknowns) * + png_sizeof(png_unknown_chunk))); + if (np == NULL) + { + png_warning(png_ptr, + "Out of memory while processing unknown chunk."); + return; + } + + png_memcpy(np, info_ptr->unknown_chunks, + info_ptr->unknown_chunks_num * png_sizeof(png_unknown_chunk)); + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks=NULL; + + for (i = 0; i < num_unknowns; i++) + { + png_unknown_chunkp to = np + info_ptr->unknown_chunks_num + i; + png_unknown_chunkp from = unknowns + i; + + png_memcpy((png_charp)to->name, + (png_charp)from->name, + png_sizeof(from->name)); + to->name[png_sizeof(to->name)-1] = '\0'; + to->size = from->size; + /* note our location in the read or write sequence */ + to->location = (png_byte)(png_ptr->mode & 0xff); + + if (from->size == 0) + to->data=NULL; + else + { + to->data = (png_bytep)png_malloc_warn(png_ptr, + (png_uint_32)from->size); + if (to->data == NULL) + { + png_warning(png_ptr, + "Out of memory while processing unknown chunk."); + to->size = 0; + } + else + png_memcpy(to->data, from->data, from->size); + } + } + + info_ptr->unknown_chunks = np; + info_ptr->unknown_chunks_num += num_unknowns; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_UNKN; +#endif +} +void PNGAPI +png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr, + int chunk, int location) +{ + if (png_ptr != NULL && info_ptr != NULL && chunk >= 0 && chunk < + (int)info_ptr->unknown_chunks_num) + info_ptr->unknown_chunks[chunk].location = (png_byte)location; +} +#endif + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +void PNGAPI +png_permit_empty_plte (png_structp png_ptr, int empty_plte_permitted) +{ + /* This function is deprecated in favor of png_permit_mng_features() + and will be removed from libpng-1.3.0 */ + png_debug(1, "in png_permit_empty_plte, DEPRECATED."); + if (png_ptr == NULL) + return; + png_ptr->mng_features_permitted = (png_byte) + ((png_ptr->mng_features_permitted & (~PNG_FLAG_MNG_EMPTY_PLTE)) | + ((empty_plte_permitted & PNG_FLAG_MNG_EMPTY_PLTE))); +} +#endif +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +png_uint_32 PNGAPI +png_permit_mng_features (png_structp png_ptr, png_uint_32 mng_features) +{ + png_debug(1, "in png_permit_mng_features"); + if (png_ptr == NULL) + return (png_uint_32)0; + png_ptr->mng_features_permitted = + (png_byte)(mng_features & PNG_ALL_MNG_FEATURES); + return (png_uint_32)png_ptr->mng_features_permitted; +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +void PNGAPI +png_set_keep_unknown_chunks(png_structp png_ptr, int keep, png_bytep + chunk_list, int num_chunks) +{ + png_bytep new_list, p; + int i, old_num_chunks; + if (png_ptr == NULL) + return; + if (num_chunks == 0) + { + if (keep == PNG_HANDLE_CHUNK_ALWAYS || keep == PNG_HANDLE_CHUNK_IF_SAFE) + png_ptr->flags |= PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + + if (keep == PNG_HANDLE_CHUNK_ALWAYS) + png_ptr->flags |= PNG_FLAG_KEEP_UNSAFE_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNSAFE_CHUNKS; + return; + } + if (chunk_list == NULL) + return; + old_num_chunks = png_ptr->num_chunk_list; + new_list=(png_bytep)png_malloc(png_ptr, + (png_uint_32) + (5*(num_chunks + old_num_chunks))); + if (png_ptr->chunk_list != NULL) + { + png_memcpy(new_list, png_ptr->chunk_list, + (png_size_t)(5*old_num_chunks)); + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + } + png_memcpy(new_list + 5*old_num_chunks, chunk_list, + (png_size_t)(5*num_chunks)); + for (p = new_list + 5*old_num_chunks + 4, i = 0; inum_chunk_list = old_num_chunks + num_chunks; + png_ptr->chunk_list = new_list; +#ifdef PNG_FREE_ME_SUPPORTED + png_ptr->free_me |= PNG_FREE_LIST; +#endif +} +#endif + +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) +void PNGAPI +png_set_read_user_chunk_fn(png_structp png_ptr, png_voidp user_chunk_ptr, + png_user_chunk_ptr read_user_chunk_fn) +{ + png_debug(1, "in png_set_read_user_chunk_fn"); + if (png_ptr == NULL) + return; + png_ptr->read_user_chunk_fn = read_user_chunk_fn; + png_ptr->user_chunk_ptr = user_chunk_ptr; +} +#endif + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_set_rows(png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers) +{ + png_debug1(1, "in %s storage function", "rows"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers)) + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); + info_ptr->row_pointers = row_pointers; + if (row_pointers) + info_ptr->valid |= PNG_INFO_IDAT; +} +#endif + +#ifdef PNG_WRITE_SUPPORTED +void PNGAPI +png_set_compression_buffer_size(png_structp png_ptr, + png_uint_32 size) +{ + if (png_ptr == NULL) + return; + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf_size = (png_size_t)size; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; +} +#endif + +void PNGAPI +png_set_invalid(png_structp png_ptr, png_infop info_ptr, int mask) +{ + if (png_ptr && info_ptr) + info_ptr->valid &= ~mask; +} + + +#ifndef PNG_1_0_X +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +/* function was added to libpng 1.2.0 and should always exist by default */ +void PNGAPI +png_set_asm_flags (png_structp png_ptr, png_uint_32 asm_flags) +{ +/* Obsolete as of libpng-1.2.20 and will be removed from libpng-1.4.0 */ + if (png_ptr != NULL) + png_ptr->asm_flags = 0; + asm_flags = asm_flags; /* Quiet the compiler */ +} + +/* this function was added to libpng 1.2.0 */ +void PNGAPI +png_set_mmx_thresholds (png_structp png_ptr, + png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold) +{ +/* Obsolete as of libpng-1.2.20 and will be removed from libpng-1.4.0 */ + if (png_ptr == NULL) + return; + /* Quiet the compiler */ + mmx_bitdepth_threshold = mmx_bitdepth_threshold; + mmx_rowbytes_threshold = mmx_rowbytes_threshold; +} +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* this function was added to libpng 1.2.6 */ +void PNGAPI +png_set_user_limits (png_structp png_ptr, png_uint_32 user_width_max, + png_uint_32 user_height_max) +{ + /* Images with dimensions larger than these limits will be + * rejected by png_set_IHDR(). To accept any PNG datastream + * regardless of dimensions, set both limits to 0x7ffffffL. + */ + if (png_ptr == NULL) return; + png_ptr->user_width_max = user_width_max; + png_ptr->user_height_max = user_height_max; +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + +#endif /* ?PNG_1_0_X */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/ext/libpng/pngtest.c b/ext/libpng/pngtest.c new file mode 100644 index 00000000..1c337beb --- /dev/null +++ b/ext/libpng/pngtest.c @@ -0,0 +1,1688 @@ + +/* pngtest.c - a simple test program to test libpng + * + * Last changed in libpng 1.2.32 [September 18, 2008] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2008 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This program reads in a PNG image, writes it out again, and then + * compares the two files. If the files are identical, this shows that + * the basic chunk handling, filtering, and (de)compression code is working + * properly. It does not currently test all of the transforms, although + * it probably should. + * + * The program will report "FAIL" in certain legitimate cases: + * 1) when the compression level or filter selection method is changed. + * 2) when the maximum IDAT size (PNG_ZBUF_SIZE in pngconf.h) is not 8192. + * 3) unknown unsafe-to-copy ancillary chunks or unknown critical chunks + * exist in the input file. + * 4) others not listed here... + * In these cases, it is best to check with another tool such as "pngcheck" + * to see what the differences between the two files are. + * + * If a filename is given on the command-line, then this file is used + * for the input, rather than the default "pngtest.png". This allows + * testing a wide variety of files easily. You can also test a number + * of files at once by typing "pngtest -m file1.png file2.png ..." + */ + +#include "png.h" + +#if defined(_WIN32_WCE) +# if _WIN32_WCE < 211 + __error__ (f|w)printf functions are not supported on old WindowsCE.; +# endif +# include +# include +# define READFILE(file, data, length, check) \ + if (ReadFile(file, data, length, &check, NULL)) check = 0 +# define WRITEFILE(file, data, length, check)) \ + if (WriteFile(file, data, length, &check, NULL)) check = 0 +# define FCLOSE(file) CloseHandle(file) +#else +# include +# include +# define READFILE(file, data, length, check) \ + check=(png_size_t)fread(data, (png_size_t)1, length, file) +# define WRITEFILE(file, data, length, check) \ + check=(png_size_t)fwrite(data, (png_size_t)1, length, file) +# define FCLOSE(file) fclose(file) +#endif + +#if defined(PNG_NO_STDIO) +# if defined(_WIN32_WCE) + typedef HANDLE png_FILE_p; +# else + typedef FILE * png_FILE_p; +# endif +#endif + +/* Makes pngtest verbose so we can find problems (needs to be before png.h) */ +#ifndef PNG_DEBUG +# define PNG_DEBUG 0 +#endif + +#if !PNG_DEBUG +# define SINGLE_ROWBUF_ALLOC /* makes buffer overruns easier to nail */ +#endif + +/* Turn on CPU timing +#define PNGTEST_TIMING +*/ + +#ifdef PNG_NO_FLOATING_POINT_SUPPORTED +#undef PNGTEST_TIMING +#endif + +#ifdef PNGTEST_TIMING +static float t_start, t_stop, t_decode, t_encode, t_misc; +#include +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +#define PNG_tIME_STRING_LENGTH 29 +static int tIME_chunk_present = 0; +static char tIME_string[PNG_tIME_STRING_LENGTH] = "tIME chunk is not present"; +#endif + +static int verbose = 0; + +int test_one_file PNGARG((PNG_CONST char *inname, PNG_CONST char *outname)); + +#ifdef __TURBOC__ +#include +#endif + +/* defined so I can write to a file on gui/windowing platforms */ +/* #define STDERR stderr */ +#define STDERR stdout /* for DOS */ + +/* In case a system header (e.g., on AIX) defined jmpbuf */ +#ifdef jmpbuf +# undef jmpbuf +#endif + +/* Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng */ +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) png_ptr->jmpbuf +#endif + +/* example of using row callbacks to make a simple progress meter */ +static int status_pass = 1; +static int status_dots_requested = 0; +static int status_dots = 1; + +void +#ifdef PNG_1_0_X +PNGAPI +#endif +read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) +{ + if (png_ptr == NULL || row_number > PNG_UINT_31_MAX) return; + if (status_pass != pass) + { + fprintf(stdout, "\n Pass %d: ", pass); + status_pass = pass; + status_dots = 31; + } + status_dots--; + if (status_dots == 0) + { + fprintf(stdout, "\n "); + status_dots=30; + } + fprintf(stdout, "r"); +} + +void +#ifdef PNG_1_0_X +PNGAPI +#endif +write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) +{ + if (png_ptr == NULL || row_number > PNG_UINT_31_MAX || pass > 7) return; + fprintf(stdout, "w"); +} + + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) +/* Example of using user transform callback (we don't transform anything, + but merely examine the row filters. We set this to 256 rather than + 5 in case illegal filter values are present.) */ +static png_uint_32 filters_used[256]; +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data) +{ + if (png_ptr != NULL && row_info != NULL) + ++filters_used[*(data - 1)]; +} +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +/* example of using user transform callback (we don't transform anything, + but merely count the zero samples) */ + +static png_uint_32 zero_samples; + +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data) +{ + png_bytep dp = data; + if (png_ptr == NULL)return; + + /* contents of row_info: + * png_uint_32 width width of row + * png_uint_32 rowbytes number of bytes in row + * png_byte color_type color type of pixels + * png_byte bit_depth bit depth of samples + * png_byte channels number of channels (1-4) + * png_byte pixel_depth bits per pixel (depth*channels) + */ + + + /* counts the number of zero samples (or zero pixels if color_type is 3 */ + + if (row_info->color_type == 0 || row_info->color_type == 3) + { + int pos = 0; + png_uint_32 n, nstop; + for (n = 0, nstop=row_info->width; nbit_depth == 1) + { + if (((*dp << pos++ ) & 0x80) == 0) zero_samples++; + if (pos == 8) + { + pos = 0; + dp++; + } + } + if (row_info->bit_depth == 2) + { + if (((*dp << (pos+=2)) & 0xc0) == 0) zero_samples++; + if (pos == 8) + { + pos = 0; + dp++; + } + } + if (row_info->bit_depth == 4) + { + if (((*dp << (pos+=4)) & 0xf0) == 0) zero_samples++; + if (pos == 8) + { + pos = 0; + dp++; + } + } + if (row_info->bit_depth == 8) + if (*dp++ == 0) zero_samples++; + if (row_info->bit_depth == 16) + { + if ((*dp | *(dp+1)) == 0) zero_samples++; + dp+=2; + } + } + } + else /* other color types */ + { + png_uint_32 n, nstop; + int channel; + int color_channels = row_info->channels; + if (row_info->color_type > 3)color_channels--; + + for (n = 0, nstop=row_info->width; nbit_depth == 8) + if (*dp++ == 0) zero_samples++; + if (row_info->bit_depth == 16) + { + if ((*dp | *(dp+1)) == 0) zero_samples++; + dp+=2; + } + } + if (row_info->color_type > 3) + { + dp++; + if (row_info->bit_depth == 16)dp++; + } + } + } +} +#endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */ + +static int wrote_question = 0; + +#if defined(PNG_NO_STDIO) +/* START of code to validate stdio-free compilation */ +/* These copies of the default read/write functions come from pngrio.c and */ +/* pngwio.c. They allow "don't include stdio" testing of the library. */ +/* This is the function that does the actual reading of data. If you are + not reading from a standard C stream, you should create a replacement + read_data function and use it at run time with png_set_read_fn(), rather + than changing the library. */ + +#ifndef USE_FAR_KEYWORD +static void +pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ + READFILE((png_FILE_p)png_ptr->io_ptr, data, length, check); + + if (check != length) + { + png_error(png_ptr, "Read Error!"); + } +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void +pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + int check; + png_byte *n_data; + png_FILE_p io_ptr; + + /* Check if data really is near. If so, use usual code. */ + n_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)n_data == data) + { + READFILE(io_ptr, n_data, length, check); + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t read, remaining, err; + check = 0; + remaining = length; + do + { + read = MIN(NEAR_BUF_SIZE, remaining); + READFILE(io_ptr, buf, 1, err); + png_memcpy(data, buf, read); /* copy far buffer to near buffer */ + if (err != read) + break; + else + check += err; + data += read; + remaining -= read; + } + while (remaining != 0); + } + if (check != length) + { + png_error(png_ptr, "read Error"); + } +} +#endif /* USE_FAR_KEYWORD */ + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +static void +pngtest_flush(png_structp png_ptr) +{ +#if !defined(_WIN32_WCE) + png_FILE_p io_ptr; + io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr)); + if (io_ptr != NULL) + fflush(io_ptr); +#endif +} +#endif + +/* This is the function that does the actual writing of data. If you are + not writing to a standard C stream, you should create a replacement + write_data function and use it at run time with png_set_write_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +static void +pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + + WRITEFILE((png_FILE_p)png_ptr->io_ptr, data, length, check); + if (check != length) + { + png_error(png_ptr, "Write Error"); + } +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void +pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ + png_FILE_p io_ptr; + + /* Check if data really is near. If so, use usual code. */ + near_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)near_data == data) + { + WRITEFILE(io_ptr, near_data, length, check); + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t written, remaining, err; + check = 0; + remaining = length; + do + { + written = MIN(NEAR_BUF_SIZE, remaining); + png_memcpy(buf, data, written); /* copy far buffer to near buffer */ + WRITEFILE(io_ptr, buf, written, err); + if (err != written) + break; + else + check += err; + data += written; + remaining -= written; + } + while (remaining != 0); + } + if (check != length) + { + png_error(png_ptr, "Write Error"); + } +} +#endif /* USE_FAR_KEYWORD */ + +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void +pngtest_warning(png_structp png_ptr, png_const_charp message) +{ + PNG_CONST char *name = "UNKNOWN (ERROR!)"; + if (png_ptr != NULL && png_ptr->error_ptr != NULL) + name = png_ptr->error_ptr; + fprintf(STDERR, "%s: libpng warning: %s\n", name, message); +} + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void +pngtest_error(png_structp png_ptr, png_const_charp message) +{ + pngtest_warning(png_ptr, message); + /* We can return because png_error calls the default handler, which is + * actually OK in this case. */ +} +#endif /* PNG_NO_STDIO */ +/* END of code to validate stdio-free compilation */ + +/* START of code to validate memory allocation and deallocation */ +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + +/* Allocate memory. For reasonable files, size should never exceed + 64K. However, zlib may allocate more then 64K if you don't tell + it not to. See zconf.h and png.h for more information. zlib does + need to allocate exactly 64K, so whatever you call here must + have the ability to do that. + + This piece of code can be compiled to validate max 64K allocations + by setting MAXSEG_64K in zlib zconf.h *or* PNG_MAX_MALLOC_64K. */ +typedef struct memory_information +{ + png_uint_32 size; + png_voidp pointer; + struct memory_information FAR *next; +} memory_information; +typedef memory_information FAR *memory_infop; + +static memory_infop pinformation = NULL; +static int current_allocation = 0; +static int maximum_allocation = 0; +static int total_allocation = 0; +static int num_allocations = 0; + +png_voidp png_debug_malloc PNGARG((png_structp png_ptr, png_uint_32 size)); +void png_debug_free PNGARG((png_structp png_ptr, png_voidp ptr)); + +png_voidp +png_debug_malloc(png_structp png_ptr, png_uint_32 size) +{ + + /* png_malloc has already tested for NULL; png_create_struct calls + png_debug_malloc directly, with png_ptr == NULL which is OK */ + + if (size == 0) + return (NULL); + + /* This calls the library allocator twice, once to get the requested + buffer and once to get a new free list entry. */ + { + /* Disable malloc_fn and free_fn */ + memory_infop pinfo; + png_set_mem_fn(png_ptr, NULL, NULL, NULL); + pinfo = (memory_infop)png_malloc(png_ptr, + (png_uint_32)png_sizeof(*pinfo)); + pinfo->size = size; + current_allocation += size; + total_allocation += size; + num_allocations ++; + if (current_allocation > maximum_allocation) + maximum_allocation = current_allocation; + pinfo->pointer = (png_voidp)png_malloc(png_ptr, size); + /* Restore malloc_fn and free_fn */ + png_set_mem_fn(png_ptr, + png_voidp_NULL, (png_malloc_ptr)png_debug_malloc, + (png_free_ptr)png_debug_free); + if (size != 0 && pinfo->pointer == NULL) + { + current_allocation -= size; + total_allocation -= size; + png_error(png_ptr, + "out of memory in pngtest->png_debug_malloc."); + } + pinfo->next = pinformation; + pinformation = pinfo; + /* Make sure the caller isn't assuming zeroed memory. */ + png_memset(pinfo->pointer, 0xdd, pinfo->size); + if (verbose) + printf("png_malloc %lu bytes at %x\n", (unsigned long)size, + pinfo->pointer); + return (png_voidp)(pinfo->pointer); + } +} + +/* Free a pointer. It is removed from the list at the same time. */ +void +png_debug_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL) + fprintf(STDERR, "NULL pointer to png_debug_free.\n"); + if (ptr == 0) + { +#if 0 /* This happens all the time. */ + fprintf(STDERR, "WARNING: freeing NULL pointer\n"); +#endif + return; + } + + /* Unlink the element from the list. */ + { + memory_infop FAR *ppinfo = &pinformation; + for (;;) + { + memory_infop pinfo = *ppinfo; + if (pinfo->pointer == ptr) + { + *ppinfo = pinfo->next; + current_allocation -= pinfo->size; + if (current_allocation < 0) + fprintf(STDERR, "Duplicate free of memory\n"); + /* We must free the list element too, but first kill + the memory that is to be freed. */ + png_memset(ptr, 0x55, pinfo->size); + png_free_default(png_ptr, pinfo); + pinfo = NULL; + break; + } + if (pinfo->next == NULL) + { + fprintf(STDERR, "Pointer %x not found\n", (unsigned int)ptr); + break; + } + ppinfo = &pinfo->next; + } + } + + /* Finally free the data. */ + if (verbose) + printf("Freeing %x\n", ptr); + png_free_default(png_ptr, ptr); + ptr = NULL; +} +#endif /* PNG_USER_MEM_SUPPORTED && PNG_DEBUG */ +/* END of code to test memory allocation/deallocation */ + + +/* Demonstration of user chunk support of the sTER and vpAg chunks */ +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + +/* (sTER is a public chunk not yet understood by libpng. vpAg is a private +chunk used in ImageMagick to store "virtual page" size). */ + +static png_uint_32 user_chunk_data[4]; + + /* 0: sTER mode + 1 + * 1: vpAg width + * 2: vpAg height + * 3: vpAg units + */ + +static int read_user_chunk_callback(png_struct *png_ptr, + png_unknown_chunkp chunk) +{ + png_uint_32 + *user_chunk_data; + + /* Return one of the following: */ + /* return (-n); chunk had an error */ + /* return (0); did not recognize */ + /* return (n); success */ + + /* The unknown chunk structure contains the chunk data: + * png_byte name[5]; + * png_byte *data; + * png_size_t size; + * + * Note that libpng has already taken care of the CRC handling. + */ + + if (chunk->name[0] == 115 && chunk->name[1] == 84 && /* s T */ + chunk->name[2] == 69 && chunk->name[3] == 82) /* E R */ + { + /* Found sTER chunk */ + if (chunk->size != 1) + return (-1); /* Error return */ + if (chunk->data[0] != 0 && chunk->data[0] != 1) + return (-1); /* Invalid mode */ + user_chunk_data=(png_uint_32 *) png_get_user_chunk_ptr(png_ptr); + user_chunk_data[0]=chunk->data[0]+1; + return (1); + } + if (chunk->name[0] != 118 || chunk->name[1] != 112 || /* v p */ + chunk->name[2] != 65 || chunk->name[3] != 103) /* A g */ + return (0); /* Did not recognize */ + + /* Found ImageMagick vpAg chunk */ + + if (chunk->size != 9) + return (-1); /* Error return */ + + user_chunk_data=(png_uint_32 *) png_get_user_chunk_ptr(png_ptr); + + user_chunk_data[1]=png_get_uint_31(png_ptr, chunk->data); + user_chunk_data[2]=png_get_uint_31(png_ptr, chunk->data + 4); + user_chunk_data[3]=(png_uint_32)chunk->data[8]; + + return (1); + +} +#endif +/* END of code to demonstrate user chunk support */ + +/* Test one file */ +int +test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) +{ + static png_FILE_p fpin; + static png_FILE_p fpout; /* "static" prevents setjmp corruption */ + png_structp read_ptr; + png_infop read_info_ptr, end_info_ptr; +#ifdef PNG_WRITE_SUPPORTED + png_structp write_ptr; + png_infop write_info_ptr; + png_infop write_end_info_ptr; +#else + png_structp write_ptr = NULL; + png_infop write_info_ptr = NULL; + png_infop write_end_info_ptr = NULL; +#endif + png_bytep row_buf; + png_uint_32 y; + png_uint_32 width, height; + int num_pass, pass; + int bit_depth, color_type; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + +#if defined(_WIN32_WCE) + TCHAR path[MAX_PATH]; +#endif + char inbuf[256], outbuf[256]; + + row_buf = NULL; + +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, inname, -1, path, MAX_PATH); + if ((fpin = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpin = fopen(inname, "rb")) == NULL) +#endif + { + fprintf(STDERR, "Could not find input file %s\n", inname); + return (1); + } + +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, outname, -1, path, MAX_PATH); + if ((fpout = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpout = fopen(outname, "wb")) == NULL) +#endif + { + fprintf(STDERR, "Could not open output file %s\n", outname); + FCLOSE(fpin); + return (1); + } + + png_debug(0, "Allocating read and write structures"); +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + read_ptr = + png_create_read_struct_2(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL, png_voidp_NULL, + (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); +#else + read_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL); +#endif +#if defined(PNG_NO_STDIO) + png_set_error_fn(read_ptr, (png_voidp)inname, pngtest_error, + pngtest_warning); +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + user_chunk_data[0] = 0; + user_chunk_data[1] = 0; + user_chunk_data[2] = 0; + user_chunk_data[3] = 0; + png_set_read_user_chunk_fn(read_ptr, user_chunk_data, + read_user_chunk_callback); + +#endif +#ifdef PNG_WRITE_SUPPORTED +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + write_ptr = + png_create_write_struct_2(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL, png_voidp_NULL, + (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); +#else + write_ptr = + png_create_write_struct(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL); +#endif +#if defined(PNG_NO_STDIO) + png_set_error_fn(write_ptr, (png_voidp)inname, pngtest_error, + pngtest_warning); +#endif +#endif + png_debug(0, "Allocating read_info, write_info and end_info structures"); + read_info_ptr = png_create_info_struct(read_ptr); + end_info_ptr = png_create_info_struct(read_ptr); +#ifdef PNG_WRITE_SUPPORTED + write_info_ptr = png_create_info_struct(write_ptr); + write_end_info_ptr = png_create_info_struct(write_ptr); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_debug(0, "Setting jmpbuf for read struct"); +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_jmpbuf(read_ptr))) +#endif + { + fprintf(STDERR, "%s -> %s: libpng read error\n", inname, outname); + png_free(read_ptr, row_buf); + row_buf = NULL; + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + png_destroy_info_struct(write_ptr, &write_end_info_ptr); + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + FCLOSE(fpin); + FCLOSE(fpout); + return (1); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(read_ptr), jmpbuf, png_sizeof(jmp_buf)); +#endif + +#ifdef PNG_WRITE_SUPPORTED + png_debug(0, "Setting jmpbuf for write struct"); +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_jmpbuf(write_ptr))) +#endif + { + fprintf(STDERR, "%s -> %s: libpng write error\n", inname, outname); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); + png_destroy_info_struct(write_ptr, &write_end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + FCLOSE(fpin); + FCLOSE(fpout); + return (1); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(write_ptr), jmpbuf, png_sizeof(jmp_buf)); +#endif +#endif +#endif + + png_debug(0, "Initializing input and output streams"); +#if !defined(PNG_NO_STDIO) + png_init_io(read_ptr, fpin); +# ifdef PNG_WRITE_SUPPORTED + png_init_io(write_ptr, fpout); +# endif +#else + png_set_read_fn(read_ptr, (png_voidp)fpin, pngtest_read_data); +# ifdef PNG_WRITE_SUPPORTED + png_set_write_fn(write_ptr, (png_voidp)fpout, pngtest_write_data, +# if defined(PNG_WRITE_FLUSH_SUPPORTED) + pngtest_flush); +# else + NULL); +# endif +# endif +#endif + if (status_dots_requested == 1) + { +#ifdef PNG_WRITE_SUPPORTED + png_set_write_status_fn(write_ptr, write_row_callback); +#endif + png_set_read_status_fn(read_ptr, read_row_callback); + } + else + { +#ifdef PNG_WRITE_SUPPORTED + png_set_write_status_fn(write_ptr, png_write_status_ptr_NULL); +#endif + png_set_read_status_fn(read_ptr, png_read_status_ptr_NULL); + } + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + { + int i; + for (i = 0; i<256; i++) + filters_used[i] = 0; + png_set_read_user_transform_fn(read_ptr, count_filters); + } +#endif +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + zero_samples = 0; + png_set_write_user_transform_fn(write_ptr, count_zero_samples); +#endif + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) +# ifndef PNG_HANDLE_CHUNK_ALWAYS +# define PNG_HANDLE_CHUNK_ALWAYS 3 +# endif + png_set_keep_unknown_chunks(read_ptr, PNG_HANDLE_CHUNK_ALWAYS, + png_bytep_NULL, 0); +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) +# ifndef PNG_HANDLE_CHUNK_IF_SAFE +# define PNG_HANDLE_CHUNK_IF_SAFE 2 +# endif + png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_IF_SAFE, + png_bytep_NULL, 0); +#endif + + png_debug(0, "Reading info struct"); + png_read_info(read_ptr, read_info_ptr); + + png_debug(0, "Transferring info struct"); + { + int interlace_type, compression_type, filter_type; + + if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth, + &color_type, &interlace_type, &compression_type, &filter_type)) + { + png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth, +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + color_type, interlace_type, compression_type, filter_type); +#else + color_type, PNG_INTERLACE_NONE, compression_type, filter_type); +#endif + } + } +#if defined(PNG_FIXED_POINT_SUPPORTED) +#if defined(PNG_cHRM_SUPPORTED) + { + png_fixed_point white_x, white_y, red_x, red_y, green_x, green_y, blue_x, + blue_y; + if (png_get_cHRM_fixed(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, + &red_y, &green_x, &green_y, &blue_x, &blue_y)) + { + png_set_cHRM_fixed(write_ptr, write_info_ptr, white_x, white_y, red_x, + red_y, green_x, green_y, blue_x, blue_y); + } + } +#endif +#if defined(PNG_gAMA_SUPPORTED) + { + png_fixed_point gamma; + + if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &gamma)) + { + png_set_gAMA_fixed(write_ptr, write_info_ptr, gamma); + } + } +#endif +#else /* Use floating point versions */ +#if defined(PNG_FLOATING_POINT_SUPPORTED) +#if defined(PNG_cHRM_SUPPORTED) + { + double white_x, white_y, red_x, red_y, green_x, green_y, blue_x, + blue_y; + if (png_get_cHRM(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, + &red_y, &green_x, &green_y, &blue_x, &blue_y)) + { + png_set_cHRM(write_ptr, write_info_ptr, white_x, white_y, red_x, + red_y, green_x, green_y, blue_x, blue_y); + } + } +#endif +#if defined(PNG_gAMA_SUPPORTED) + { + double gamma; + + if (png_get_gAMA(read_ptr, read_info_ptr, &gamma)) + { + png_set_gAMA(write_ptr, write_info_ptr, gamma); + } + } +#endif +#endif /* floating point */ +#endif /* fixed point */ +#if defined(PNG_iCCP_SUPPORTED) + { + png_charp name; + png_charp profile; + png_uint_32 proflen; + int compression_type; + + if (png_get_iCCP(read_ptr, read_info_ptr, &name, &compression_type, + &profile, &proflen)) + { + png_set_iCCP(write_ptr, write_info_ptr, name, compression_type, + profile, proflen); + } + } +#endif +#if defined(PNG_sRGB_SUPPORTED) + { + int intent; + + if (png_get_sRGB(read_ptr, read_info_ptr, &intent)) + { + png_set_sRGB(write_ptr, write_info_ptr, intent); + } + } +#endif + { + png_colorp palette; + int num_palette; + + if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette)) + { + png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette); + } + } +#if defined(PNG_bKGD_SUPPORTED) + { + png_color_16p background; + + if (png_get_bKGD(read_ptr, read_info_ptr, &background)) + { + png_set_bKGD(write_ptr, write_info_ptr, background); + } + } +#endif +#if defined(PNG_hIST_SUPPORTED) + { + png_uint_16p hist; + + if (png_get_hIST(read_ptr, read_info_ptr, &hist)) + { + png_set_hIST(write_ptr, write_info_ptr, hist); + } + } +#endif +#if defined(PNG_oFFs_SUPPORTED) + { + png_int_32 offset_x, offset_y; + int unit_type; + + if (png_get_oFFs(read_ptr, read_info_ptr, &offset_x, &offset_y, + &unit_type)) + { + png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y, unit_type); + } + } +#endif +#if defined(PNG_pCAL_SUPPORTED) + { + png_charp purpose, units; + png_charpp params; + png_int_32 X0, X1; + int type, nparams; + + if (png_get_pCAL(read_ptr, read_info_ptr, &purpose, &X0, &X1, &type, + &nparams, &units, ¶ms)) + { + png_set_pCAL(write_ptr, write_info_ptr, purpose, X0, X1, type, + nparams, units, params); + } + } +#endif +#if defined(PNG_pHYs_SUPPORTED) + { + png_uint_32 res_x, res_y; + int unit_type; + + if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y, &unit_type)) + { + png_set_pHYs(write_ptr, write_info_ptr, res_x, res_y, unit_type); + } + } +#endif +#if defined(PNG_sBIT_SUPPORTED) + { + png_color_8p sig_bit; + + if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit)) + { + png_set_sBIT(write_ptr, write_info_ptr, sig_bit); + } + } +#endif +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + { + int unit; + double scal_width, scal_height; + + if (png_get_sCAL(read_ptr, read_info_ptr, &unit, &scal_width, + &scal_height)) + { + png_set_sCAL(write_ptr, write_info_ptr, unit, scal_width, scal_height); + } + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + { + int unit; + png_charp scal_width, scal_height; + + if (png_get_sCAL_s(read_ptr, read_info_ptr, &unit, &scal_width, + &scal_height)) + { + png_set_sCAL_s(write_ptr, write_info_ptr, unit, scal_width, scal_height); + } + } +#endif +#endif +#endif +#if defined(PNG_TEXT_SUPPORTED) + { + png_textp text_ptr; + int num_text; + + if (png_get_text(read_ptr, read_info_ptr, &text_ptr, &num_text) > 0) + { + png_debug1(0, "Handling %d iTXt/tEXt/zTXt chunks", num_text); + png_set_text(write_ptr, write_info_ptr, text_ptr, num_text); + } + } +#endif +#if defined(PNG_tIME_SUPPORTED) + { + png_timep mod_time; + + if (png_get_tIME(read_ptr, read_info_ptr, &mod_time)) + { + png_set_tIME(write_ptr, write_info_ptr, mod_time); +#if defined(PNG_TIME_RFC1123_SUPPORTED) + /* we have to use png_memcpy instead of "=" because the string + pointed to by png_convert_to_rfc1123() gets free'ed before + we use it */ + png_memcpy(tIME_string, + png_convert_to_rfc1123(read_ptr, mod_time), + png_sizeof(tIME_string)); + tIME_string[png_sizeof(tIME_string) - 1] = '\0'; + tIME_chunk_present++; +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + } + } +#endif +#if defined(PNG_tRNS_SUPPORTED) + { + png_bytep trans; + int num_trans; + png_color_16p trans_values; + + if (png_get_tRNS(read_ptr, read_info_ptr, &trans, &num_trans, + &trans_values)) + { + int sample_max = (1 << read_info_ptr->bit_depth); + /* libpng doesn't reject a tRNS chunk with out-of-range samples */ + if (!((read_info_ptr->color_type == PNG_COLOR_TYPE_GRAY && + (int)trans_values->gray > sample_max) || + (read_info_ptr->color_type == PNG_COLOR_TYPE_RGB && + ((int)trans_values->red > sample_max || + (int)trans_values->green > sample_max || + (int)trans_values->blue > sample_max)))) + png_set_tRNS(write_ptr, write_info_ptr, trans, num_trans, + trans_values); + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + { + png_unknown_chunkp unknowns; + int num_unknowns = (int)png_get_unknown_chunks(read_ptr, read_info_ptr, + &unknowns); + if (num_unknowns) + { + png_size_t i; + png_set_unknown_chunks(write_ptr, write_info_ptr, unknowns, + num_unknowns); + /* copy the locations from the read_info_ptr. The automatically + generated locations in write_info_ptr are wrong because we + haven't written anything yet */ + for (i = 0; i < (png_size_t)num_unknowns; i++) + png_set_unknown_chunk_location(write_ptr, write_info_ptr, i, + unknowns[i].location); + } + } +#endif + +#ifdef PNG_WRITE_SUPPORTED + png_debug(0, "Writing info struct"); + +/* If we wanted, we could write info in two steps: + png_write_info_before_PLTE(write_ptr, write_info_ptr); + */ + png_write_info(write_ptr, write_info_ptr); + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + if (user_chunk_data[0] != 0) + { + png_byte png_sTER[5] = {115, 84, 69, 82, '\0'}; + + unsigned char + ster_chunk_data[1]; + + if (verbose) + fprintf(STDERR, "stereo mode = %lu\n", + (unsigned long)(user_chunk_data[0] - 1)); + ster_chunk_data[0]=(unsigned char)(user_chunk_data[0] - 1); + png_write_chunk(write_ptr, png_sTER, ster_chunk_data, 1); + } + if (user_chunk_data[1] != 0 || user_chunk_data[2] != 0) + { + png_byte png_vpAg[5] = {118, 112, 65, 103, '\0'}; + + unsigned char + vpag_chunk_data[9]; + + if (verbose) + fprintf(STDERR, "vpAg = %lu x %lu, units=%lu\n", + (unsigned long)user_chunk_data[1], + (unsigned long)user_chunk_data[2], + (unsigned long)user_chunk_data[3]); + png_save_uint_32(vpag_chunk_data, user_chunk_data[1]); + png_save_uint_32(vpag_chunk_data + 4, user_chunk_data[2]); + vpag_chunk_data[8] = (unsigned char)(user_chunk_data[3] & 0xff); + png_write_chunk(write_ptr, png_vpAg, vpag_chunk_data, 9); + } + +#endif +#endif + +#ifdef SINGLE_ROWBUF_ALLOC + png_debug(0, "Allocating row buffer..."); + row_buf = (png_bytep)png_malloc(read_ptr, + png_get_rowbytes(read_ptr, read_info_ptr)); + png_debug1(0, "0x%08lx", (unsigned long)row_buf); +#endif /* SINGLE_ROWBUF_ALLOC */ + png_debug(0, "Writing row data"); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) + num_pass = png_set_interlace_handling(read_ptr); +# ifdef PNG_WRITE_SUPPORTED + png_set_interlace_handling(write_ptr); +# endif +#else + num_pass = 1; +#endif + +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_misc += (t_stop - t_start); + t_start = t_stop; +#endif + for (pass = 0; pass < num_pass; pass++) + { + png_debug1(0, "Writing row data for pass %d", pass); + for (y = 0; y < height; y++) + { +#ifndef SINGLE_ROWBUF_ALLOC + png_debug2(0, "Allocating row buffer (pass %d, y = %ld)...", pass, y); + row_buf = (png_bytep)png_malloc(read_ptr, + png_get_rowbytes(read_ptr, read_info_ptr)); + png_debug2(0, "0x%08lx (%ld bytes)", (unsigned long)row_buf, + png_get_rowbytes(read_ptr, read_info_ptr)); +#endif /* !SINGLE_ROWBUF_ALLOC */ + png_read_rows(read_ptr, (png_bytepp)&row_buf, png_bytepp_NULL, 1); + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_decode += (t_stop - t_start); + t_start = t_stop; +#endif + png_write_rows(write_ptr, (png_bytepp)&row_buf, 1); +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_encode += (t_stop - t_start); + t_start = t_stop; +#endif +#endif /* PNG_WRITE_SUPPORTED */ + +#ifndef SINGLE_ROWBUF_ALLOC + png_debug2(0, "Freeing row buffer (pass %d, y = %ld)", pass, y); + png_free(read_ptr, row_buf); + row_buf = NULL; +#endif /* !SINGLE_ROWBUF_ALLOC */ + } + } + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + png_free_data(read_ptr, read_info_ptr, PNG_FREE_UNKN, -1); +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + png_free_data(write_ptr, write_info_ptr, PNG_FREE_UNKN, -1); +#endif + + png_debug(0, "Reading and writing end_info data"); + + png_read_end(read_ptr, end_info_ptr); +#if defined(PNG_TEXT_SUPPORTED) + { + png_textp text_ptr; + int num_text; + + if (png_get_text(read_ptr, end_info_ptr, &text_ptr, &num_text) > 0) + { + png_debug1(0, "Handling %d iTXt/tEXt/zTXt chunks", num_text); + png_set_text(write_ptr, write_end_info_ptr, text_ptr, num_text); + } + } +#endif +#if defined(PNG_tIME_SUPPORTED) + { + png_timep mod_time; + + if (png_get_tIME(read_ptr, end_info_ptr, &mod_time)) + { + png_set_tIME(write_ptr, write_end_info_ptr, mod_time); +#if defined(PNG_TIME_RFC1123_SUPPORTED) + /* we have to use png_memcpy instead of "=" because the string + pointed to by png_convert_to_rfc1123() gets free'ed before + we use it */ + png_memcpy(tIME_string, + png_convert_to_rfc1123(read_ptr, mod_time), + png_sizeof(tIME_string)); + tIME_string[png_sizeof(tIME_string) - 1] = '\0'; + tIME_chunk_present++; +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + { + png_unknown_chunkp unknowns; + int num_unknowns; + num_unknowns = (int)png_get_unknown_chunks(read_ptr, end_info_ptr, + &unknowns); + if (num_unknowns) + { + png_size_t i; + png_set_unknown_chunks(write_ptr, write_end_info_ptr, unknowns, + num_unknowns); + /* copy the locations from the read_info_ptr. The automatically + generated locations in write_end_info_ptr are wrong because we + haven't written the end_info yet */ + for (i = 0; i < (png_size_t)num_unknowns; i++) + png_set_unknown_chunk_location(write_ptr, write_end_info_ptr, i, + unknowns[i].location); + } + } +#endif +#ifdef PNG_WRITE_SUPPORTED + png_write_end(write_ptr, write_end_info_ptr); +#endif + +#ifdef PNG_EASY_ACCESS_SUPPORTED + if (verbose) + { + png_uint_32 iwidth, iheight; + iwidth = png_get_image_width(write_ptr, write_info_ptr); + iheight = png_get_image_height(write_ptr, write_info_ptr); + fprintf(STDERR, "Image width = %lu, height = %lu\n", + (unsigned long)iwidth, (unsigned long)iheight); + } +#endif + + png_debug(0, "Destroying data structs"); +#ifdef SINGLE_ROWBUF_ALLOC + png_debug(1, "destroying row_buf for read_ptr"); + png_free(read_ptr, row_buf); + row_buf = NULL; +#endif /* SINGLE_ROWBUF_ALLOC */ + png_debug(1, "destroying read_ptr, read_info_ptr, end_info_ptr"); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + png_debug(1, "destroying write_end_info_ptr"); + png_destroy_info_struct(write_ptr, &write_end_info_ptr); + png_debug(1, "destroying write_ptr, write_info_ptr"); + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + png_debug(0, "Destruction complete."); + + FCLOSE(fpin); + FCLOSE(fpout); + + png_debug(0, "Opening files for comparison"); +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, inname, -1, path, MAX_PATH); + if ((fpin = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpin = fopen(inname, "rb")) == NULL) +#endif + { + fprintf(STDERR, "Could not find file %s\n", inname); + return (1); + } + +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, outname, -1, path, MAX_PATH); + if ((fpout = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpout = fopen(outname, "rb")) == NULL) +#endif + { + fprintf(STDERR, "Could not find file %s\n", outname); + FCLOSE(fpin); + return (1); + } + + for (;;) + { + png_size_t num_in, num_out; + + READFILE(fpin, inbuf, 1, num_in); + READFILE(fpout, outbuf, 1, num_out); + + if (num_in != num_out) + { + fprintf(STDERR, "\nFiles %s and %s are of a different size\n", + inname, outname); + if (wrote_question == 0) + { + fprintf(STDERR, + " Was %s written with the same maximum IDAT chunk size (%d bytes),", + inname, PNG_ZBUF_SIZE); + fprintf(STDERR, + "\n filtering heuristic (libpng default), compression"); + fprintf(STDERR, + " level (zlib default),\n and zlib version (%s)?\n\n", + ZLIB_VERSION); + wrote_question = 1; + } + FCLOSE(fpin); + FCLOSE(fpout); + return (0); + } + + if (!num_in) + break; + + if (png_memcmp(inbuf, outbuf, num_in)) + { + fprintf(STDERR, "\nFiles %s and %s are different\n", inname, outname); + if (wrote_question == 0) + { + fprintf(STDERR, + " Was %s written with the same maximum IDAT chunk size (%d bytes),", + inname, PNG_ZBUF_SIZE); + fprintf(STDERR, + "\n filtering heuristic (libpng default), compression"); + fprintf(STDERR, + " level (zlib default),\n and zlib version (%s)?\n\n", + ZLIB_VERSION); + wrote_question = 1; + } + FCLOSE(fpin); + FCLOSE(fpout); + return (0); + } + } + + FCLOSE(fpin); + FCLOSE(fpout); + + return (0); +} + +/* input and output filenames */ +#ifdef RISCOS +static PNG_CONST char *inname = "pngtest/png"; +static PNG_CONST char *outname = "pngout/png"; +#else +static PNG_CONST char *inname = "pngtest.png"; +static PNG_CONST char *outname = "pngout.png"; +#endif + +int +main(int argc, char *argv[]) +{ + int multiple = 0; + int ierror = 0; + + fprintf(STDERR, "Testing libpng version %s\n", PNG_LIBPNG_VER_STRING); + fprintf(STDERR, " with zlib version %s\n", ZLIB_VERSION); + fprintf(STDERR, "%s", png_get_copyright(NULL)); + /* Show the version of libpng used in building the library */ + fprintf(STDERR, " library (%lu):%s", + (unsigned long)png_access_version_number(), + png_get_header_version(NULL)); + /* Show the version of libpng used in building the application */ + fprintf(STDERR, " pngtest (%lu):%s", (unsigned long)PNG_LIBPNG_VER, + PNG_HEADER_VERSION_STRING); + fprintf(STDERR, " sizeof(png_struct)=%ld, sizeof(png_info)=%ld\n", + (long)png_sizeof(png_struct), (long)png_sizeof(png_info)); + + /* Do some consistency checking on the memory allocation settings, I'm + not sure this matters, but it is nice to know, the first of these + tests should be impossible because of the way the macros are set + in pngconf.h */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) + fprintf(STDERR, " NOTE: Zlib compiled for max 64k, libpng not\n"); +#endif + /* I think the following can happen. */ +#if !defined(MAXSEG_64K) && defined(PNG_MAX_MALLOC_64K) + fprintf(STDERR, " NOTE: libpng compiled for max 64k, zlib not\n"); +#endif + + if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING)) + { + fprintf(STDERR, + "Warning: versions are different between png.h and png.c\n"); + fprintf(STDERR, " png.h version: %s\n", PNG_LIBPNG_VER_STRING); + fprintf(STDERR, " png.c version: %s\n\n", png_libpng_ver); + ++ierror; + } + + if (argc > 1) + { + if (strcmp(argv[1], "-m") == 0) + { + multiple = 1; + status_dots_requested = 0; + } + else if (strcmp(argv[1], "-mv") == 0 || + strcmp(argv[1], "-vm") == 0 ) + { + multiple = 1; + verbose = 1; + status_dots_requested = 1; + } + else if (strcmp(argv[1], "-v") == 0) + { + verbose = 1; + status_dots_requested = 1; + inname = argv[2]; + } + else + { + inname = argv[1]; + status_dots_requested = 0; + } + } + + if (!multiple && argc == 3 + verbose) + outname = argv[2 + verbose]; + + if ((!multiple && argc > 3 + verbose) || (multiple && argc < 2)) + { + fprintf(STDERR, + "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n", + argv[0], argv[0]); + fprintf(STDERR, + " reads/writes one PNG file (without -m) or multiple files (-m)\n"); + fprintf(STDERR, + " with -m %s is used as a temporary file\n", outname); + exit(1); + } + + if (multiple) + { + int i; +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + int allocation_now = current_allocation; +#endif + for (i=2; isize, + (unsigned int) pinfo->pointer); + pinfo = pinfo->next; + } + } +#endif + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + fprintf(STDERR, " Current memory allocation: %10d bytes\n", + current_allocation); + fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", + maximum_allocation); + fprintf(STDERR, " Total memory allocation: %10d bytes\n", + total_allocation); + fprintf(STDERR, " Number of allocations: %10d\n", + num_allocations); +#endif + } + else + { + int i; + for (i = 0; i<3; ++i) + { + int kerror; +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + int allocation_now = current_allocation; +#endif + if (i == 1) status_dots_requested = 1; + else if (verbose == 0)status_dots_requested = 0; + if (i == 0 || verbose == 1 || ierror != 0) + fprintf(STDERR, "Testing %s:", inname); + kerror = test_one_file(inname, outname); + if (kerror == 0) + { + if (verbose == 1 || i == 2) + { +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + int k; +#endif +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + fprintf(STDERR, "\n PASS (%lu zero samples)\n", + (unsigned long)zero_samples); +#else + fprintf(STDERR, " PASS\n"); +#endif +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + for (k = 0; k<256; k++) + if (filters_used[k]) + fprintf(STDERR, " Filter %d was used %lu times\n", + k, + (unsigned long)filters_used[k]); +#endif +#if defined(PNG_TIME_RFC1123_SUPPORTED) + if (tIME_chunk_present != 0) + fprintf(STDERR, " tIME = %s\n", tIME_string); +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + } + } + else + { + if (verbose == 0 && i != 2) + fprintf(STDERR, "Testing %s:", inname); + fprintf(STDERR, " FAIL\n"); + ierror += kerror; + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + if (allocation_now != current_allocation) + fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n", + current_allocation - allocation_now); + if (current_allocation != 0) + { + memory_infop pinfo = pinformation; + + fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n", + current_allocation); + while (pinfo != NULL) + { + fprintf(STDERR, " %lu bytes at %x\n", + (unsigned long)pinfo->size, (unsigned int)pinfo->pointer); + pinfo = pinfo->next; + } + } +#endif + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + fprintf(STDERR, " Current memory allocation: %10d bytes\n", + current_allocation); + fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", + maximum_allocation); + fprintf(STDERR, " Total memory allocation: %10d bytes\n", + total_allocation); + fprintf(STDERR, " Number of allocations: %10d\n", + num_allocations); +#endif + } + +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_misc += (t_stop - t_start); + t_start = t_stop; + fprintf(STDERR, " CPU time used = %.3f seconds", + (t_misc+t_decode+t_encode)/(float)CLOCKS_PER_SEC); + fprintf(STDERR, " (decoding %.3f,\n", + t_decode/(float)CLOCKS_PER_SEC); + fprintf(STDERR, " encoding %.3f ,", + t_encode/(float)CLOCKS_PER_SEC); + fprintf(STDERR, " other %.3f seconds)\n\n", + t_misc/(float)CLOCKS_PER_SEC); +#endif + + if (ierror == 0) + fprintf(STDERR, "libpng passes test\n"); + else + fprintf(STDERR, "libpng FAILS test\n"); + return (int)(ierror != 0); +} + +/* Generate a compiler error if there is an old png.h in the search path. */ +typedef version_1_2_35 your_png_h_is_not_version_1_2_35; diff --git a/ext/libpng/pngtrans.c b/ext/libpng/pngtrans.c new file mode 100644 index 00000000..bd1ec5ad --- /dev/null +++ b/ext/libpng/pngtrans.c @@ -0,0 +1,662 @@ + +/* pngtrans.c - transforms the data in a row (used by both readers and writers) + * + * Last changed in libpng 1.2.30 [August 15, 2008] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2008 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* turn on BGR-to-RGB mapping */ +void PNGAPI +png_set_bgr(png_structp png_ptr) +{ + png_debug(1, "in png_set_bgr"); + if (png_ptr == NULL) return; + png_ptr->transformations |= PNG_BGR; +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* turn on 16 bit byte swapping */ +void PNGAPI +png_set_swap(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap"); + if (png_ptr == NULL) return; + if (png_ptr->bit_depth == 16) + png_ptr->transformations |= PNG_SWAP_BYTES; +} +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* turn on pixel packing */ +void PNGAPI +png_set_packing(png_structp png_ptr) +{ + png_debug(1, "in png_set_packing"); + if (png_ptr == NULL) return; + if (png_ptr->bit_depth < 8) + { + png_ptr->transformations |= PNG_PACK; + png_ptr->usr_bit_depth = 8; + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* turn on packed pixel swapping */ +void PNGAPI +png_set_packswap(png_structp png_ptr) +{ + png_debug(1, "in png_set_packswap"); + if (png_ptr == NULL) return; + if (png_ptr->bit_depth < 8) + png_ptr->transformations |= PNG_PACKSWAP; +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +void PNGAPI +png_set_shift(png_structp png_ptr, png_color_8p true_bits) +{ + png_debug(1, "in png_set_shift"); + if (png_ptr == NULL) return; + png_ptr->transformations |= PNG_SHIFT; + png_ptr->shift = *true_bits; +} +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +int PNGAPI +png_set_interlace_handling(png_structp png_ptr) +{ + png_debug(1, "in png_set_interlace handling"); + if (png_ptr && png_ptr->interlaced) + { + png_ptr->transformations |= PNG_INTERLACE; + return (7); + } + + return (1); +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte on read, or remove a filler or alpha byte on write. + * The filler type has changed in v0.95 to allow future 2-byte fillers + * for 48-bit input data, as well as to avoid problems with some compilers + * that don't like bytes as parameters. + */ +void PNGAPI +png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_filler"); + if (png_ptr == NULL) return; + png_ptr->transformations |= PNG_FILLER; + png_ptr->filler = (png_byte)filler; + if (filler_loc == PNG_FILLER_AFTER) + png_ptr->flags |= PNG_FLAG_FILLER_AFTER; + else + png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; + + /* This should probably go in the "do_read_filler" routine. + * I attempted to do that in libpng-1.0.1a but that caused problems + * so I restored it in libpng-1.0.2a + */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_ptr->usr_channels = 4; + } + + /* Also I added this in libpng-1.0.2a (what happens when we expand + * a less-than-8-bit grayscale to GA? */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8) + { + png_ptr->usr_channels = 2; + } +} + +#if !defined(PNG_1_0_X) +/* Added to libpng-1.2.7 */ +void PNGAPI +png_set_add_alpha(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_add_alpha"); + if (png_ptr == NULL) return; + png_set_filler(png_ptr, filler, filler_loc); + png_ptr->transformations |= PNG_ADD_ALPHA; +} +#endif + +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void PNGAPI +png_set_swap_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap_alpha"); + if (png_ptr == NULL) return; + png_ptr->transformations |= PNG_SWAP_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void PNGAPI +png_set_invert_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_alpha"); + if (png_ptr == NULL) return; + png_ptr->transformations |= PNG_INVERT_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +void PNGAPI +png_set_invert_mono(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_mono"); + if (png_ptr == NULL) return; + png_ptr->transformations |= PNG_INVERT_MONO; +} + +/* invert monochrome grayscale data */ +void /* PRIVATE */ +png_do_invert(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_invert"); + /* This test removed from libpng version 1.0.13 and 1.2.0: + * if (row_info->bit_depth == 1 && + */ +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row == NULL || row_info == NULL) + return; +#endif + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(~(*rp)); + rp++; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 8) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=2) + { + *rp = (png_byte)(~(*rp)); + rp+=2; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=4) + { + *rp = (png_byte)(~(*rp)); + *(rp+1) = (png_byte)(~(*(rp+1))); + rp+=4; + } + } +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* swaps byte order on 16 bit depth images */ +void /* PRIVATE */ +png_do_swap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_swap"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop= row_info->width * row_info->channels; + + for (i = 0; i < istop; i++, rp += 2) + { + png_byte t = *rp; + *rp = *(rp + 1); + *(rp + 1) = t; + } + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +static PNG_CONST png_byte onebppswaptable[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + +static PNG_CONST png_byte twobppswaptable[256] = { + 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0, + 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0, + 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4, + 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4, + 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8, + 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, + 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC, + 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, + 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1, + 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1, + 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5, + 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5, + 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9, + 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9, + 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD, + 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD, + 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2, + 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2, + 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6, + 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6, + 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA, + 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA, + 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE, + 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE, + 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3, + 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3, + 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7, + 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7, + 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB, + 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, + 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF, + 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF +}; + +static PNG_CONST png_byte fourbppswaptable[256] = { + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, + 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, + 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, + 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, + 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, + 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, + 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, + 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, + 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, + 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, + 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, + 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, + 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, + 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, + 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, + 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, + 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, + 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, + 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, + 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, + 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, + 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, + 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, + 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, + 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, + 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, + 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, + 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, + 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, + 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, + 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, + 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF +}; + +/* swaps pixel packing order within bytes */ +void /* PRIVATE */ +png_do_packswap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_packswap"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->bit_depth < 8) + { + png_bytep rp, end, table; + + end = row + row_info->rowbytes; + + if (row_info->bit_depth == 1) + table = (png_bytep)onebppswaptable; + else if (row_info->bit_depth == 2) + table = (png_bytep)twobppswaptable; + else if (row_info->bit_depth == 4) + table = (png_bytep)fourbppswaptable; + else + return; + + for (rp = row; rp < end; rp++) + *rp = table[*rp]; + } +} +#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */ + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +/* remove filler or alpha byte(s) */ +void /* PRIVATE */ +png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) +{ + png_debug(1, "in png_do_strip_filler"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_bytep sp=row; + png_bytep dp=row; + png_uint_32 row_width=row_info->width; + png_uint_32 i; + + if ((row_info->color_type == PNG_COLOR_TYPE_RGB || + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 4) + { + if (row_info->bit_depth == 8) + { + /* This converts from RGBX or RGBA to RGB */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + dp+=3; sp+=4; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp++; + } + } + /* This converts from XRGB or ARGB to RGB */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from RRGGBBXX or RRGGBBAA to RRGGBB */ + sp += 8; dp += 6; + for (i = 1; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXRRGGBB or AARRGGBB to RRGGBB */ + for (i = 0; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + sp+=2; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 48; + row_info->rowbytes = row_width * 6; + } + row_info->channels = 3; + } + else if ((row_info->color_type == PNG_COLOR_TYPE_GRAY || + (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 2) + { + if (row_info->bit_depth == 8) + { + /* This converts from GX or GA to G */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + for (i = 0; i < row_width; i++) + { + *dp++ = *sp++; + sp++; + } + } + /* This converts from XG or AG to G */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from GGXX or GGAA to GG */ + sp += 4; dp += 2; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXGG or AAGG to GG */ + for (i = 0; i < row_width; i++) + { + sp += 2; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + row_info->channels = 1; + } + if (flags & PNG_FLAG_STRIP_ALPHA) + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + } +} +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* swaps red and blue bytes within a pixel */ +void /* PRIVATE */ +png_do_bgr(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_bgr"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 3) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 4) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + } + else if (row_info->bit_depth == 16) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 6) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 8) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + } + } +} +#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +void PNGAPI +png_set_user_transform_info(png_structp png_ptr, png_voidp + user_transform_ptr, int user_transform_depth, int user_transform_channels) +{ + png_debug(1, "in png_set_user_transform_info"); + if (png_ptr == NULL) return; +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + png_ptr->user_transform_ptr = user_transform_ptr; + png_ptr->user_transform_depth = (png_byte)user_transform_depth; + png_ptr->user_transform_channels = (png_byte)user_transform_channels; +#else + if (user_transform_ptr || user_transform_depth || user_transform_channels) + png_warning(png_ptr, + "This version of libpng does not support user transform info"); +#endif +} +#endif + +/* This function returns a pointer to the user_transform_ptr associated with + * the user transform functions. The application should free any memory + * associated with this pointer before png_write_destroy and png_read_destroy + * are called. + */ +png_voidp PNGAPI +png_get_user_transform_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) return (NULL); +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + return ((png_voidp)png_ptr->user_transform_ptr); +#else + return (NULL); +#endif +} +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/ext/libpng/pngvcrd.c b/ext/libpng/pngvcrd.c new file mode 100644 index 00000000..54fef609 --- /dev/null +++ b/ext/libpng/pngvcrd.c @@ -0,0 +1 @@ +/* pnggvrd.c was removed from libpng-1.2.20. */ diff --git a/ext/libpng/pngwio.c b/ext/libpng/pngwio.c new file mode 100644 index 00000000..67e415a0 --- /dev/null +++ b/ext/libpng/pngwio.c @@ -0,0 +1,234 @@ + +/* pngwio.c - functions for data output + * + * Last changed in libpng 1.2.35 [February 14, 2009] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2009 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all output. Users who need + * special handling are expected to write functions that have the same + * arguments as these and perform similar functions, but that possibly + * use different output methods. Note that you shouldn't change these + * functions, but rather write replacement functions and then change + * them at run time with png_set_write_fn(...). + */ + +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Write the data to whatever output you are using. The default routine + writes to a file pointer. Note that this routine sometimes gets called + with very small lengths, so you should implement some kind of simple + buffering if you are using unbuffered writes. This should never be asked + to write more than 64K on a 16 bit machine. */ + +void /* PRIVATE */ +png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + if (png_ptr->write_data_fn != NULL ) + (*(png_ptr->write_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL write function"); +} + +#if !defined(PNG_NO_STDIO) +/* This is the function that does the actual writing of data. If you are + not writing to a standard C stream, you should create a replacement + write_data function and use it at run time with png_set_write_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + + if (png_ptr == NULL) return; +#if defined(_WIN32_WCE) + if ( !WriteFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); +#endif + if (check != length) + png_error(png_ptr, "Write Error"); +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ + png_FILE_p io_ptr; + + if (png_ptr == NULL) return; + /* Check if data really is near. If so, use usual code. */ + near_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)near_data == data) + { +#if defined(_WIN32_WCE) + if ( !WriteFile(io_ptr, near_data, length, &check, NULL) ) + check = 0; +#else + check = fwrite(near_data, 1, length, io_ptr); +#endif + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t written, remaining, err; + check = 0; + remaining = length; + do + { + written = MIN(NEAR_BUF_SIZE, remaining); + png_memcpy(buf, data, written); /* copy far buffer to near buffer */ +#if defined(_WIN32_WCE) + if ( !WriteFile(io_ptr, buf, written, &err, NULL) ) + err = 0; +#else + err = fwrite(buf, 1, written, io_ptr); +#endif + if (err != written) + break; + else + check += err; + data += written; + remaining -= written; + } + while (remaining != 0); + } + if (check != length) + png_error(png_ptr, "Write Error"); +} + +#endif +#endif + +/* This function is called to output any data pending writing (normally + to disk). After png_flush is called, there should be no data pending + writing in any buffers. */ +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +void /* PRIVATE */ +png_flush(png_structp png_ptr) +{ + if (png_ptr->output_flush_fn != NULL) + (*(png_ptr->output_flush_fn))(png_ptr); +} + +#if !defined(PNG_NO_STDIO) +void PNGAPI +png_default_flush(png_structp png_ptr) +{ +#if !defined(_WIN32_WCE) + png_FILE_p io_ptr; +#endif + if (png_ptr == NULL) return; +#if !defined(_WIN32_WCE) + io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr)); + if (io_ptr != NULL && fileno(io_ptr) != -1) + fflush(io_ptr); +#endif +} +#endif +#endif + +/* This function allows the application to supply new output functions for + libpng if standard C streams aren't being used. + + This function takes as its arguments: + png_ptr - pointer to a png output data structure + io_ptr - pointer to user supplied structure containing info about + the output functions. May be NULL. + write_data_fn - pointer to a new output function that takes as its + arguments a pointer to a png_struct, a pointer to + data to be written, and a 32-bit unsigned int that is + the number of bytes to be written. The new write + function should call png_error(png_ptr, "Error msg") + to exit and output any fatal error messages. + flush_data_fn - pointer to a new flush function that takes as its + arguments a pointer to a png_struct. After a call to + the flush function, there should be no data in any buffers + or pending transmission. If the output method doesn't do + any buffering of ouput, a function prototype must still be + supplied although it doesn't have to do anything. If + PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile + time, output_flush_fn will be ignored, although it must be + supplied for compatibility. */ +void PNGAPI +png_set_write_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) +{ + if (png_ptr == NULL) return; + png_ptr->io_ptr = io_ptr; + +#if !defined(PNG_NO_STDIO) + if (write_data_fn != NULL) + png_ptr->write_data_fn = write_data_fn; + else + png_ptr->write_data_fn = png_default_write_data; +#else + png_ptr->write_data_fn = write_data_fn; +#endif + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#if !defined(PNG_NO_STDIO) + if (output_flush_fn != NULL) + png_ptr->output_flush_fn = output_flush_fn; + else + png_ptr->output_flush_fn = png_default_flush; +#else + png_ptr->output_flush_fn = output_flush_fn; +#endif +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + + /* It is an error to read while writing a png file */ + if (png_ptr->read_data_fn != NULL) + { + png_ptr->read_data_fn = NULL; + png_warning(png_ptr, + "Attempted to set both read_data_fn and write_data_fn in"); + png_warning(png_ptr, + "the same structure. Resetting read_data_fn to NULL."); + } +} + +#if defined(USE_FAR_KEYWORD) +#if defined(_MSC_VER) +void *png_far_to_near(png_structp png_ptr, png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + FP_OFF(near_ptr) = FP_OFF(ptr); + far_ptr = (void FAR *)near_ptr; + if (check != 0) + if (FP_SEG(ptr) != FP_SEG(far_ptr)) + png_error(png_ptr, "segment lost in conversion"); + return(near_ptr); +} +# else +void *png_far_to_near(png_structp png_ptr, png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + near_ptr = (void FAR *)ptr; + far_ptr = (void FAR *)near_ptr; + if (check != 0) + if (far_ptr != ptr) + png_error(png_ptr, "segment lost in conversion"); + return(near_ptr); +} +# endif +# endif +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/ext/libpng/pngwrite.c b/ext/libpng/pngwrite.c new file mode 100644 index 00000000..fb80516e --- /dev/null +++ b/ext/libpng/pngwrite.c @@ -0,0 +1,1547 @@ + +/* pngwrite.c - general routines to write a PNG file + * + * Last changed in libpng 1.2.34 [December 18, 2008] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2008 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +/* get internal access to png.h */ +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Writes all the PNG information. This is the suggested way to use the + * library. If you have a new chunk to add, make a function to write it, + * and put it in the correct location here. If you want the chunk written + * after the image data, put it in png_write_end(). I strongly encourage + * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing + * the chunk, as that will keep the code from breaking if you want to just + * write a plain PNG file. If you have long comments, I suggest writing + * them in png_write_end(), and compressing them. + */ +void PNGAPI +png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_info_before_PLTE"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + { + png_write_sig(png_ptr); /* write PNG signature */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&(png_ptr->mng_features_permitted)) + { + png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); + png_ptr->mng_features_permitted=0; + } +#endif + /* write IHDR information. */ + png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, + info_ptr->filter_type, +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + info_ptr->interlace_type); +#else + 0); +#endif + /* the rest of these check to see if the valid field has the appropriate + flag set, and if it does, writes the chunk. */ +#if defined(PNG_WRITE_gAMA_SUPPORTED) + if (info_ptr->valid & PNG_INFO_gAMA) + { +# ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_gAMA(png_ptr, info_ptr->gamma); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_gAMA_fixed(png_ptr, info_ptr->int_gamma); +# endif +#endif + } +#endif +#if defined(PNG_WRITE_sRGB_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sRGB) + png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent); +#endif +#if defined(PNG_WRITE_iCCP_SUPPORTED) + if (info_ptr->valid & PNG_INFO_iCCP) + png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE, + info_ptr->iccp_profile, (int)info_ptr->iccp_proflen); +#endif +#if defined(PNG_WRITE_sBIT_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sBIT) + png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); +#endif +#if defined(PNG_WRITE_cHRM_SUPPORTED) + if (info_ptr->valid & PNG_INFO_cHRM) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_cHRM(png_ptr, + info_ptr->x_white, info_ptr->y_white, + info_ptr->x_red, info_ptr->y_red, + info_ptr->x_green, info_ptr->y_green, + info_ptr->x_blue, info_ptr->y_blue); +#else +# ifdef PNG_FIXED_POINT_SUPPORTED + png_write_cHRM_fixed(png_ptr, + info_ptr->int_x_white, info_ptr->int_y_white, + info_ptr->int_x_red, info_ptr->int_y_red, + info_ptr->int_x_green, info_ptr->int_y_green, + info_ptr->int_x_blue, info_ptr->int_y_blue); +# endif +#endif + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && !(up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + if (up->size == 0) + png_warning(png_ptr, "Writing zero-length unknown chunk"); + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; + } +} + +void PNGAPI +png_write_info(png_structp png_ptr, png_infop info_ptr) +{ +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) + int i; +#endif + + png_debug(1, "in png_write_info"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_write_info_before_PLTE(png_ptr, info_ptr); + + if (info_ptr->valid & PNG_INFO_PLTE) + png_write_PLTE(png_ptr, info_ptr->palette, + (png_uint_32)info_ptr->num_palette); + else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Valid palette required for paletted images"); + +#if defined(PNG_WRITE_tRNS_SUPPORTED) + if (info_ptr->valid & PNG_INFO_tRNS) + { +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel (in tRNS) */ + if ((png_ptr->transformations & PNG_INVERT_ALPHA) && + info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + int j; + for (j=0; j<(int)info_ptr->num_trans; j++) + info_ptr->trans[j] = (png_byte)(255 - info_ptr->trans[j]); + } +#endif + png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values), + info_ptr->num_trans, info_ptr->color_type); + } +#endif +#if defined(PNG_WRITE_bKGD_SUPPORTED) + if (info_ptr->valid & PNG_INFO_bKGD) + png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); +#endif +#if defined(PNG_WRITE_hIST_SUPPORTED) + if (info_ptr->valid & PNG_INFO_hIST) + png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); +#endif +#if defined(PNG_WRITE_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, + info_ptr->offset_unit_type); +#endif +#if defined(PNG_WRITE_pCAL_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pCAL) + png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, + info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, + info_ptr->pcal_units, info_ptr->pcal_params); +#endif +#if defined(PNG_WRITE_sCAL_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sCAL) +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) + png_write_sCAL(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_pixel_width, info_ptr->scal_pixel_height); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_s_width, info_ptr->scal_s_height); +#else + png_warning(png_ptr, + "png_write_sCAL not supported; sCAL chunk not written."); +#endif +#endif +#endif +#if defined(PNG_WRITE_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, + info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); +#endif +#if defined(PNG_WRITE_tIME_SUPPORTED) + if (info_ptr->valid & PNG_INFO_tIME) + { + png_write_tIME(png_ptr, &(info_ptr->mod_time)); + png_ptr->mode |= PNG_WROTE_tIME; + } +#endif +#if defined(PNG_WRITE_sPLT_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sPLT) + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); +#endif +#if defined(PNG_WRITE_TEXT_SUPPORTED) + /* Check to see if we need to write text chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing header text chunk %d, type %d", i, + info_ptr->text[i].compression); + /* an internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#if defined(PNG_WRITE_iTXt_SUPPORTED) + /* write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + /* If we want a compressed text chunk */ + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) + { +#if defined(PNG_WRITE_zTXt_SUPPORTED) + /* write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#if defined(PNG_WRITE_tEXt_SUPPORTED) + /* write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, + 0); +#else + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif +} + +/* Writes the end of the PNG file. If you don't want to write comments or + * time information, you can pass NULL for info. If you already wrote these + * in png_write_info(), do not write them again here. If you have long + * comments, I suggest writing them here, and compressing them. + */ +void PNGAPI +png_write_end(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_end"); + if (png_ptr == NULL) + return; + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "No IDATs written into file"); + + /* see if user wants us to write information chunks */ + if (info_ptr != NULL) + { +#if defined(PNG_WRITE_TEXT_SUPPORTED) + int i; /* local index variable */ +#endif +#if defined(PNG_WRITE_tIME_SUPPORTED) + /* check to see if user has supplied a time chunk */ + if ((info_ptr->valid & PNG_INFO_tIME) && + !(png_ptr->mode & PNG_WROTE_tIME)) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); +#endif +#if defined(PNG_WRITE_TEXT_SUPPORTED) + /* loop through comment chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing trailer text chunk %d, type %d", i, + info_ptr->text[i].compression); + /* an internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#if defined(PNG_WRITE_iTXt_SUPPORTED) + /* write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt) + { +#if defined(PNG_WRITE_zTXt_SUPPORTED) + /* write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#if defined(PNG_WRITE_tEXt_SUPPORTED) + /* write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0); +#else + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_AFTER_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + } + + png_ptr->mode |= PNG_AFTER_IDAT; + + /* write end of PNG file */ + png_write_IEND(png_ptr); + /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03, + * and restored again in libpng-1.2.30, may cause some applications that + * do not set png_ptr->output_flush_fn to crash. If your application + * experiences a problem, please try building libpng with + * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to + * png-mng-implement at lists.sf.net . This kludge will be removed + * from libpng-1.4.0. + */ +#if defined(PNG_WRITE_FLUSH_SUPPORTED) && \ + defined(PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED) + png_flush(png_ptr); +#endif +} + +#if defined(PNG_WRITE_tIME_SUPPORTED) +#if !defined(_WIN32_WCE) +/* "time.h" functions are not supported on WindowsCE */ +void PNGAPI +png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime) +{ + png_debug(1, "in png_convert_from_struct_tm"); + ptime->year = (png_uint_16)(1900 + ttime->tm_year); + ptime->month = (png_byte)(ttime->tm_mon + 1); + ptime->day = (png_byte)ttime->tm_mday; + ptime->hour = (png_byte)ttime->tm_hour; + ptime->minute = (png_byte)ttime->tm_min; + ptime->second = (png_byte)ttime->tm_sec; +} + +void PNGAPI +png_convert_from_time_t(png_timep ptime, time_t ttime) +{ + struct tm *tbuf; + + png_debug(1, "in png_convert_from_time_t"); + tbuf = gmtime(&ttime); + png_convert_from_struct_tm(ptime, tbuf); +} +#endif +#endif + +/* Initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +} + +/* Alternate initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ +#ifdef PNG_SETJMP_SUPPORTED + volatile +#endif + png_structp png_ptr; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + int i; + png_debug(1, "in png_create_write_struct"); +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif /* PNG_USER_MEM_SUPPORTED */ + if (png_ptr == NULL) + return (NULL); + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_ptr->jmpbuf)) +#endif + { + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf=NULL; + png_destroy_struct(png_ptr); + return (NULL); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_ptr->jmpbuf, jmpbuf, png_sizeof(jmp_buf)); +#endif +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif /* PNG_USER_MEM_SUPPORTED */ + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + if (user_png_ver) + { + i=0; + do + { + if (user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + } + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[80]; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "Incompatible libpng version in application and library"); + } + } + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + + png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, + png_flush_ptr_NULL); + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, png_doublep_NULL, png_doublep_NULL); +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* Applications that neglect to set up their own setjmp() and then encounter + a png_error() will longjmp here. Since the jmpbuf is then meaningless we + abort instead of returning. */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) + PNG_ABORT(); + png_memcpy(png_ptr->jmpbuf, jmpbuf, png_sizeof(jmp_buf)); +#else + if (setjmp(png_ptr->jmpbuf)) + PNG_ABORT(); +#endif +#endif + return (png_ptr); +} + +/* Initialize png_ptr structure, and allocate any memory needed */ +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* Deprecated. */ +#undef png_write_init +void PNGAPI +png_write_init(png_structp png_ptr) +{ + /* We only come here via pre-1.0.7-compiled applications */ + png_write_init_2(png_ptr, "1.0.6 or earlier", 0, 0); +} + +void PNGAPI +png_write_init_2(png_structp png_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size, png_size_t png_info_size) +{ + /* We only come here via pre-1.0.12-compiled applications */ + if (png_ptr == NULL) return; +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + if (png_sizeof(png_struct) > png_struct_size || + png_sizeof(png_info) > png_info_size) + { + char msg[80]; + png_ptr->warning_fn=NULL; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); + } +#endif + if (png_sizeof(png_struct) > png_struct_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The png struct allocated by the application for writing is too small."); + } + if (png_sizeof(png_info) > png_info_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The info struct allocated by the application for writing is too small."); + } + png_write_init_3(&png_ptr, user_png_ver, png_struct_size); +} +#endif /* PNG_1_0_X || PNG_1_2_X */ + + +void PNGAPI +png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size) +{ + png_structp png_ptr=*ptr_ptr; +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* to save current jump buffer */ +#endif + + int i = 0; + + if (png_ptr == NULL) + return; + + do + { + if (user_png_ver[i] != png_libpng_ver[i]) + { +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; +#else + png_ptr->warning_fn=NULL; + png_warning(png_ptr, + "Application uses deprecated png_write_init() and should be recompiled."); + break; +#endif + } + } while (png_libpng_ver[i++]); + + png_debug(1, "in png_write_init_3"); + +#ifdef PNG_SETJMP_SUPPORTED + /* save jump buffer and error functions */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof(jmp_buf)); +#endif + + if (png_sizeof(png_struct) > png_struct_size) + { + png_destroy_struct(png_ptr); + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); + *ptr_ptr = png_ptr; + } + + /* reset all variables to 0 */ + png_memset(png_ptr, 0, png_sizeof(png_struct)); + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* restore jump buffer */ + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof(jmp_buf)); +#endif + + png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, + png_flush_ptr_NULL); + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, png_doublep_NULL, png_doublep_NULL); +#endif +} + +/* Write a few rows of image data. If the image is interlaced, + * either you will have to write the 7 sub images, or, if you + * have called png_set_interlace_handling(), you will have to + * "write" the image seven times. + */ +void PNGAPI +png_write_rows(png_structp png_ptr, png_bytepp row, + png_uint_32 num_rows) +{ + png_uint_32 i; /* row counter */ + png_bytepp rp; /* row pointer */ + + png_debug(1, "in png_write_rows"); + + if (png_ptr == NULL) + return; + + /* loop through the rows */ + for (i = 0, rp = row; i < num_rows; i++, rp++) + { + png_write_row(png_ptr, *rp); + } +} + +/* Write the image. You only need to call this function once, even + * if you are writing an interlaced image. + */ +void PNGAPI +png_write_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i; /* row index */ + int pass, num_pass; /* pass variables */ + png_bytepp rp; /* points to current row */ + + if (png_ptr == NULL) + return; + + png_debug(1, "in png_write_image"); +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* intialize interlace handling. If image is not interlaced, + this will set pass to 1 */ + num_pass = png_set_interlace_handling(png_ptr); +#else + num_pass = 1; +#endif + /* loop through passes */ + for (pass = 0; pass < num_pass; pass++) + { + /* loop through image */ + for (i = 0, rp = image; i < png_ptr->height; i++, rp++) + { + png_write_row(png_ptr, *rp); + } + } +} + +/* called by user to write a row of image data */ +void PNGAPI +png_write_row(png_structp png_ptr, png_bytep row) +{ + if (png_ptr == NULL) + return; + png_debug2(1, "in png_write_row (row %ld, pass %d)", + png_ptr->row_number, png_ptr->pass); + + /* initialize transformations and other stuff if first time */ + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* make sure we wrote the header info */ + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + png_error(png_ptr, + "png_write_info was never called before png_write_row."); + + /* check for transforms that have been set but were defined out */ +#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, "PNG_WRITE_PACKSWAP_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined."); +#endif + + png_write_start_row(png_ptr); + } + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* if interlaced and not interested in row, return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 0x03) || png_ptr->width < 3) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 0x03) != 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 0x01) || png_ptr->width < 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 0x01)) + { + png_write_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + /* set up row info for transformations */ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->usr_width; + png_ptr->row_info.channels = png_ptr->usr_channels; + png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth; + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_debug1(3, "row_info->color_type = %d", png_ptr->row_info.color_type); + png_debug1(3, "row_info->width = %lu", png_ptr->row_info.width); + png_debug1(3, "row_info->channels = %d", png_ptr->row_info.channels); + png_debug1(3, "row_info->bit_depth = %d", png_ptr->row_info.bit_depth); + png_debug1(3, "row_info->pixel_depth = %d", png_ptr->row_info.pixel_depth); + png_debug1(3, "row_info->rowbytes = %lu", png_ptr->row_info.rowbytes); + + /* Copy user's row into buffer, leaving room for filter byte. */ + png_memcpy_check(png_ptr, png_ptr->row_buf + 1, row, + png_ptr->row_info.rowbytes); + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* handle interlacing */ + if (png_ptr->interlaced && png_ptr->pass < 6 && + (png_ptr->transformations & PNG_INTERLACE)) + { + png_do_write_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass); + /* this should always get caught above, but still ... */ + if (!(png_ptr->row_info.width)) + { + png_write_finish_row(png_ptr); + return; + } + } +#endif + + /* handle other transformations */ + if (png_ptr->transformations) + png_do_write_transformations(png_ptr); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_write_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + /* Find a filter if necessary, filter the row and write it out. */ + png_write_find_filter(png_ptr, &(png_ptr->row_info)); + + if (png_ptr->write_row_fn != NULL) + (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +/* Set the automatic flush interval or 0 to turn flushing off */ +void PNGAPI +png_set_flush(png_structp png_ptr, int nrows) +{ + png_debug(1, "in png_set_flush"); + if (png_ptr == NULL) + return; + png_ptr->flush_dist = (nrows < 0 ? 0 : nrows); +} + +/* flush the current output buffers now */ +void PNGAPI +png_write_flush(png_structp png_ptr) +{ + int wrote_IDAT; + + png_debug(1, "in png_write_flush"); + if (png_ptr == NULL) + return; + /* We have already written out all of the data */ + if (png_ptr->row_number >= png_ptr->num_rows) + return; + + do + { + int ret; + + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH); + wrote_IDAT = 0; + + /* check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + if (!(png_ptr->zstream.avail_out)) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + wrote_IDAT = 1; + } + } while(wrote_IDAT == 1); + + /* If there is any data left to be output, write it into a new IDAT */ + if (png_ptr->zbuf_size != png_ptr->zstream.avail_out) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + png_ptr->flush_rows = 0; + png_flush(png_ptr); +} +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + +/* free all memory used by the write */ +void PNGAPI +png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn = NULL; + png_voidp mem_ptr = NULL; +#endif + + png_debug(1, "in png_destroy_write_struct"); + if (png_ptr_ptr != NULL) + { + png_ptr = *png_ptr_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + } + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr != NULL) + { + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; + } +#endif + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + if (png_ptr != NULL) + { + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + png_ptr->num_chunk_list = 0; + } +#endif + } + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { + png_write_destroy(png_ptr); +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + + +/* Free any memory used in png_ptr struct (old method) */ +void /* PRIVATE */ +png_write_destroy(png_structp png_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* save jump buffer */ +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_write_destroy"); + /* free any memory zlib uses */ + deflateEnd(&png_ptr->zstream); + + /* free our memory. png_free checks NULL for us. */ + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->row_buf); +#ifndef PNG_NO_WRITE_FILTER + png_free(png_ptr, png_ptr->prev_row); + png_free(png_ptr, png_ptr->sub_row); + png_free(png_ptr, png_ptr->up_row); + png_free(png_ptr, png_ptr->avg_row); + png_free(png_ptr, png_ptr->paeth_row); +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_free(png_ptr, png_ptr->time_buffer); +#endif + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_free(png_ptr, png_ptr->prev_filters); + png_free(png_ptr, png_ptr->filter_weights); + png_free(png_ptr, png_ptr->inv_filter_weights); + png_free(png_ptr, png_ptr->filter_costs); + png_free(png_ptr, png_ptr->inv_filter_costs); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* reset structure */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof(jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof(png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof(jmp_buf)); +#endif +} + +/* Allow the application to select one or more row filters to use. */ +void PNGAPI +png_set_filter(png_structp png_ptr, int method, int filters) +{ + png_debug(1, "in png_set_filter"); + if (png_ptr == NULL) + return; +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (method == PNG_INTRAPIXEL_DIFFERENCING)) + method = PNG_FILTER_TYPE_BASE; +#endif + if (method == PNG_FILTER_TYPE_BASE) + { + switch (filters & (PNG_ALL_FILTERS | 0x07)) + { +#ifndef PNG_NO_WRITE_FILTER + case 5: + case 6: + case 7: png_warning(png_ptr, "Unknown row filter for method 0"); +#endif /* PNG_NO_WRITE_FILTER */ + case PNG_FILTER_VALUE_NONE: + png_ptr->do_filter=PNG_FILTER_NONE; break; +#ifndef PNG_NO_WRITE_FILTER + case PNG_FILTER_VALUE_SUB: + png_ptr->do_filter=PNG_FILTER_SUB; break; + case PNG_FILTER_VALUE_UP: + png_ptr->do_filter=PNG_FILTER_UP; break; + case PNG_FILTER_VALUE_AVG: + png_ptr->do_filter=PNG_FILTER_AVG; break; + case PNG_FILTER_VALUE_PAETH: + png_ptr->do_filter=PNG_FILTER_PAETH; break; + default: png_ptr->do_filter = (png_byte)filters; break; +#else + default: png_warning(png_ptr, "Unknown row filter for method 0"); +#endif /* PNG_NO_WRITE_FILTER */ + } + + /* If we have allocated the row_buf, this means we have already started + * with the image and we should have allocated all of the filter buffers + * that have been selected. If prev_row isn't already allocated, then + * it is too late to start using the filters that need it, since we + * will be missing the data in the previous row. If an application + * wants to start and stop using particular filters during compression, + * it should start out with all of the filters, and then add and + * remove them after the start of compression. + */ + if (png_ptr->row_buf != NULL) + { +#ifndef PNG_NO_WRITE_FILTER + if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Up filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_UP; + } + else + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Average filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_AVG; + } + else + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_PAETH) && + png_ptr->paeth_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Paeth filter after starting"); + png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH); + } + else + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } + + if (png_ptr->do_filter == PNG_NO_FILTERS) +#endif /* PNG_NO_WRITE_FILTER */ + png_ptr->do_filter = PNG_FILTER_NONE; + } + } + else + png_error(png_ptr, "Unknown custom filter method"); +} + +/* This allows us to influence the way in which libpng chooses the "best" + * filter for the current scanline. While the "minimum-sum-of-absolute- + * differences metric is relatively fast and effective, there is some + * question as to whether it can be improved upon by trying to keep the + * filtered data going to zlib more consistent, hopefully resulting in + * better compression. + */ +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* GRR 970116 */ +void PNGAPI +png_set_filter_heuristics(png_structp png_ptr, int heuristic_method, + int num_weights, png_doublep filter_weights, + png_doublep filter_costs) +{ + int i; + + png_debug(1, "in png_set_filter_heuristics"); + if (png_ptr == NULL) + return; + if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST) + { + png_warning(png_ptr, "Unknown filter heuristic method"); + return; + } + + if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT) + { + heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED; + } + + if (num_weights < 0 || filter_weights == NULL || + heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED) + { + num_weights = 0; + } + + png_ptr->num_prev_filters = (png_byte)num_weights; + png_ptr->heuristic_method = (png_byte)heuristic_method; + + if (num_weights > 0) + { + if (png_ptr->prev_filters == NULL) + { + png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_byte) * num_weights)); + + /* To make sure that the weighting starts out fairly */ + for (i = 0; i < num_weights; i++) + { + png_ptr->prev_filters[i] = 255; + } + } + + if (png_ptr->filter_weights == NULL) + { + png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + + png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + for (i = 0; i < num_weights; i++) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + } + + for (i = 0; i < num_weights; i++) + { + if (filter_weights[i] < 0.0) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + else + { + png_ptr->inv_filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5); + png_ptr->filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5); + } + } + } + + /* If, in the future, there are other filter methods, this would + * need to be based on png_ptr->filter. + */ + if (png_ptr->filter_costs == NULL) + { + png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + } + + /* Here is where we set the relative costs of the different filters. We + * should take the desired compression level into account when setting + * the costs, so that Paeth, for instance, has a high relative cost at low + * compression levels, while it has a lower relative cost at higher + * compression settings. The filter types are in order of increasing + * relative cost, so it would be possible to do this with an algorithm. + */ + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + if (filter_costs == NULL || filter_costs[i] < 0.0) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + else if (filter_costs[i] >= 1.0) + { + png_ptr->inv_filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5); + png_ptr->filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5); + } + } +} +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +void PNGAPI +png_set_compression_level(png_structp png_ptr, int level) +{ + png_debug(1, "in png_set_compression_level"); + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL; + png_ptr->zlib_level = level; +} + +void PNGAPI +png_set_compression_mem_level(png_structp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_compression_mem_level"); + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL; + png_ptr->zlib_mem_level = mem_level; +} + +void PNGAPI +png_set_compression_strategy(png_structp png_ptr, int strategy) +{ + png_debug(1, "in png_set_compression_strategy"); + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; + png_ptr->zlib_strategy = strategy; +} + +void PNGAPI +png_set_compression_window_bits(png_structp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + if (window_bits > 15) + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + else if (window_bits < 8) + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); +#ifndef WBITS_8_OK + /* avoid libpng bug with 256-byte windows */ + if (window_bits == 8) + { + png_warning(png_ptr, "Compression window is being reset to 512"); + window_bits=9; + } +#endif + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS; + png_ptr->zlib_window_bits = window_bits; +} + +void PNGAPI +png_set_compression_method(png_structp png_ptr, int method) +{ + png_debug(1, "in png_set_compression_method"); + if (png_ptr == NULL) + return; + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD; + png_ptr->zlib_method = method; +} + +void PNGAPI +png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->write_row_fn = write_row_fn; +} + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +void PNGAPI +png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr + write_user_transform_fn) +{ + png_debug(1, "in png_set_write_user_transform_fn"); + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->write_user_transform_fn = write_user_transform_fn; +} +#endif + + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_write_png(png_structp png_ptr, png_infop info_ptr, + int transforms, voidp params) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel from opacity to transparency */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* Write the file header information. */ + png_write_info(png_ptr, info_ptr); + + /* ------ these transformations don't touch the info structure ------- */ + +#if defined(PNG_WRITE_INVERT_SUPPORTED) + /* invert monochrome pixels */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) + /* Shift the pixels up to a legal bit depth and fill in + * as appropriate to correctly scale the image. + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && (info_ptr->valid & PNG_INFO_sBIT)) + png_set_shift(png_ptr, &info_ptr->sig_bit); +#endif + +#if defined(PNG_WRITE_PACK_SUPPORTED) + /* pack pixels into bytes */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) + /* swap location of alpha bytes from ARGB to RGBA */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) + /* Pack XRGB/RGBX/ARGB/RGBA into * RGB (4 channels -> 3 channels) */ + if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) + png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); + else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); +#endif + +#if defined(PNG_WRITE_BGR_SUPPORTED) + /* flip BGR pixels to RGB */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#if defined(PNG_WRITE_SWAP_SUPPORTED) + /* swap bytes of 16-bit files to most significant byte first */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) + /* swap bits of 1, 2, 4 bit packed pixel formats */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + + /* ----------------------- end of transformations ------------------- */ + + /* write the bits */ + if (info_ptr->valid & PNG_INFO_IDAT) + png_write_image(png_ptr, info_ptr->row_pointers); + + /* It is REQUIRED to call this to finish writing the rest of the file */ + png_write_end(png_ptr, info_ptr); + + transforms = transforms; /* quiet compiler warnings */ + params = params; +} +#endif +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/ext/libpng/pngwtran.c b/ext/libpng/pngwtran.c new file mode 100644 index 00000000..3c3fe432 --- /dev/null +++ b/ext/libpng/pngwtran.c @@ -0,0 +1,572 @@ + +/* pngwtran.c - transforms the data in a row for PNG writers + * + * Last changed in libpng 1.2.9 April 14, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Transform the data according to the user's wishes. The order of + * transformations is significant. + */ +void /* PRIVATE */ +png_do_write_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_write_transformations"); + + if (png_ptr == NULL) + return; + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + if (png_ptr->write_user_transform_fn != NULL) + (*(png_ptr->write_user_transform_fn)) /* user write transform function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#endif +#if defined(PNG_WRITE_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->flags); +#endif +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->bit_depth); +#endif +#if defined(PNG_WRITE_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +} + +#if defined(PNG_WRITE_PACK_SUPPORTED) +/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The + * row_info bit depth should be 8 (one pixel per byte). The channels + * should be 1 (this only happens on grayscale and paletted images). + */ +void /* PRIVATE */ +png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) +{ + png_debug(1, "in png_do_pack"); + if (row_info->bit_depth == 8 && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->channels == 1) + { + switch ((int)bit_depth) + { + case 1: + { + png_bytep sp, dp; + int mask, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + mask = 0x80; + v = 0; + + for (i = 0; i < row_width; i++) + { + if (*sp != 0) + v |= mask; + sp++; + if (mask > 1) + mask >>= 1; + else + { + mask = 0x80; + *dp = (png_byte)v; + dp++; + v = 0; + } + } + if (mask != 0x80) + *dp = (png_byte)v; + break; + } + case 2: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 6; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x03); + v |= (value << shift); + if (shift == 0) + { + shift = 6; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 2; + sp++; + } + if (shift != 6) + *dp = (png_byte)v; + break; + } + case 4: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 4; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x0f); + v |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 4; + + sp++; + } + if (shift != 4) + *dp = (png_byte)v; + break; + } + } + row_info->bit_depth = (png_byte)bit_depth; + row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Shift pixel values to take advantage of whole range. Pass the + * true number of bits in bit_depth. The row should be packed + * according to row_info->bit_depth. Thus, if you had a row of + * bit depth 4, but the pixels only had values from 0 to 7, you + * would pass 3 as bit_depth, and this routine would translate the + * data to 0 to 15. + */ +void /* PRIVATE */ +png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) +{ + png_debug(1, "in png_do_shift"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && +#else + if ( +#endif + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift_start[4], shift_dec[4]; + int channels = 0; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift_start[channels] = row_info->bit_depth - bit_depth->red; + shift_dec[channels] = bit_depth->red; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->green; + shift_dec[channels] = bit_depth->green; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->blue; + shift_dec[channels] = bit_depth->blue; + channels++; + } + else + { + shift_start[channels] = row_info->bit_depth - bit_depth->gray; + shift_dec[channels] = bit_depth->gray; + channels++; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift_start[channels] = row_info->bit_depth - bit_depth->alpha; + shift_dec[channels] = bit_depth->alpha; + channels++; + } + + /* with low row depths, could only be grayscale, so one channel */ + if (row_info->bit_depth < 8) + { + png_bytep bp = row; + png_uint_32 i; + png_byte mask; + png_uint_32 row_bytes = row_info->rowbytes; + + if (bit_depth->gray == 1 && row_info->bit_depth == 2) + mask = 0x55; + else if (row_info->bit_depth == 4 && bit_depth->gray == 3) + mask = 0x11; + else + mask = 0xff; + + for (i = 0; i < row_bytes; i++, bp++) + { + png_uint_16 v; + int j; + + v = *bp; + *bp = 0; + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & mask); + } + } + } + else if (row_info->bit_depth == 8) + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (i = 0; i < istop; i++, bp++) + { + + png_uint_16 v; + int j; + int c = (int)(i%channels); + + v = *bp; + *bp = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & 0xff); + } + } + } + else + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (bp = row, i = 0; i < istop; i++) + { + int c = (int)(i%channels); + png_uint_16 value, v; + int j; + + v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1)); + value = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + value |= (png_uint_16)((v << j) & (png_uint_16)0xffff); + else + value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff); + } + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + } + } +} +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_swap_alpha"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from ARGB to RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AARRGGBB to RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from AG to GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AAGG to GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + } +} +#endif + +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_invert_alpha"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=3; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=6; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=2; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + } +} +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +/* undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_write_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_intrapixel"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((*rp - *(rp+1))&0xff); + *(rp+2) = (png_byte)((*(rp+2) - *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); + png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); + png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); + png_uint_32 red = (png_uint_32)((s0 - s1) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/ext/libpng/pngwutil.c b/ext/libpng/pngwutil.c new file mode 100644 index 00000000..647438e4 --- /dev/null +++ b/ext/libpng/pngwutil.c @@ -0,0 +1,2785 @@ + +/* pngwutil.c - utilities to write a PNG file + * + * Last changed in libpng 1.2.34 [December 18, 2008] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2008 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Place a 32-bit number into a buffer in PNG byte order. We work + * with unsigned numbers for convenience, although one supported + * ancillary chunk uses signed (two's complement) numbers. + */ +void PNGAPI +png_save_uint_32(png_bytep buf, png_uint_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +/* The png_save_int_32 function assumes integers are stored in two's + * complement format. If this isn't the case, then this routine needs to + * be modified to write data in two's complement format. + */ +void PNGAPI +png_save_int_32(png_bytep buf, png_int_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +void PNGAPI +png_save_uint_16(png_bytep buf, unsigned int i) +{ + buf[0] = (png_byte)((i >> 8) & 0xff); + buf[1] = (png_byte)(i & 0xff); +} + +/* Simple function to write the signature. If we have already written + * the magic bytes of the signature, or more likely, the PNG stream is + * being embedded into another stream and doesn't need its own signature, + * we should call png_set_sig_bytes() to tell libpng how many of the + * bytes have already been written. + */ +void /* PRIVATE */ +png_write_sig(png_structp png_ptr) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + + /* write the rest of the 8 byte signature */ + png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], + (png_size_t)(8 - png_ptr->sig_bytes)); + if (png_ptr->sig_bytes < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; +} + +/* Write a PNG chunk all at once. The type is an array of ASCII characters + * representing the chunk name. The array must be at least 4 bytes in + * length, and does not need to be null terminated. To be safe, pass the + * pre-defined chunk names here, and if you need a new one, define it + * where the others are defined. The length is the length of the data. + * All the data must be present. If that is not possible, use the + * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end() + * functions instead. + */ +void PNGAPI +png_write_chunk(png_structp png_ptr, png_bytep chunk_name, + png_bytep data, png_size_t length) +{ + if (png_ptr == NULL) return; + png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length); + png_write_chunk_data(png_ptr, data, (png_size_t)length); + png_write_chunk_end(png_ptr); +} + +/* Write the start of a PNG chunk. The type is the chunk type. + * The total_length is the sum of the lengths of all the data you will be + * passing in png_write_chunk_data(). + */ +void PNGAPI +png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name, + png_uint_32 length) +{ + png_byte buf[8]; + + png_debug2(0, "Writing %s chunk, length = %lu", chunk_name, + (unsigned long)length); + if (png_ptr == NULL) return; + + /* write the length and the chunk name */ + png_save_uint_32(buf, length); + png_memcpy(buf + 4, chunk_name, 4); + png_write_data(png_ptr, buf, (png_size_t)8); + /* put the chunk name into png_ptr->chunk_name */ + png_memcpy(png_ptr->chunk_name, chunk_name, 4); + /* reset the crc and run it over the chunk name */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, chunk_name, (png_size_t)4); +} + +/* Write the data of a PNG chunk started with png_write_chunk_start(). + * Note that multiple calls to this function are allowed, and that the + * sum of the lengths from these calls *must* add up to the total_length + * given to png_write_chunk_start(). + */ +void PNGAPI +png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + /* write the data, and run the CRC over it */ + if (png_ptr == NULL) return; + if (data != NULL && length > 0) + { + png_write_data(png_ptr, data, length); + /* update the CRC after writing the data, + * in case that the user I/O routine alters it. + */ + png_calculate_crc(png_ptr, data, length); + } +} + +/* Finish a chunk started with png_write_chunk_start(). */ +void PNGAPI +png_write_chunk_end(png_structp png_ptr) +{ + png_byte buf[4]; + + if (png_ptr == NULL) return; + + /* write the crc in a single operation */ + png_save_uint_32(buf, png_ptr->crc); + + png_write_data(png_ptr, buf, (png_size_t)4); +} + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED) +/* + * This pair of functions encapsulates the operation of (a) compressing a + * text string, and (b) issuing it later as a series of chunk data writes. + * The compression_state structure is shared context for these functions + * set up by the caller in order to make the whole mess thread-safe. + */ + +typedef struct +{ + char *input; /* the uncompressed input data */ + int input_len; /* its length */ + int num_output_ptr; /* number of output pointers used */ + int max_output_ptr; /* size of output_ptr */ + png_charpp output_ptr; /* array of pointers to output */ +} compression_state; + +/* compress given text into storage in the png_ptr structure */ +static int /* PRIVATE */ +png_text_compress(png_structp png_ptr, + png_charp text, png_size_t text_len, int compression, + compression_state *comp) +{ + int ret; + + comp->num_output_ptr = 0; + comp->max_output_ptr = 0; + comp->output_ptr = NULL; + comp->input = NULL; + comp->input_len = 0; + + /* we may just want to pass the text right through */ + if (compression == PNG_TEXT_COMPRESSION_NONE) + { + comp->input = text; + comp->input_len = text_len; + return((int)text_len); + } + + if (compression >= PNG_TEXT_COMPRESSION_LAST) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[50]; + png_snprintf(msg, 50, "Unknown compression type %d", compression); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "Unknown compression type"); +#endif + } + + /* We can't write the chunk until we find out how much data we have, + * which means we need to run the compressor first and save the + * output. This shouldn't be a problem, as the vast majority of + * comments should be reasonable, but we will set up an array of + * malloc'd pointers to be sure. + * + * If we knew the application was well behaved, we could simplify this + * greatly by assuming we can always malloc an output buffer large + * enough to hold the compressed text ((1001 * text_len / 1000) + 12) + * and malloc this directly. The only time this would be a bad idea is + * if we can't malloc more than 64K and we have 64K of random input + * data, or if the input string is incredibly large (although this + * wouldn't cause a failure, just a slowdown due to swapping). + */ + + /* set up the compression buffers */ + png_ptr->zstream.avail_in = (uInt)text_len; + png_ptr->zstream.next_in = (Bytef *)text; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = (Bytef *)png_ptr->zbuf; + + /* this is the same compression loop as in png_write_row() */ + do + { + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + if (ret != Z_OK) + { + /* error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* make sure the output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32) + (comp->max_output_ptr * png_sizeof(png_charpp))); + png_memcpy(comp->output_ptr, old_ptr, old_max + * png_sizeof(png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32) + (comp->max_output_ptr * png_sizeof(png_charp))); + } + + /* save the data */ + comp->output_ptr[comp->num_output_ptr] = + (png_charp)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + /* continue until we don't have any more to compress */ + } while (png_ptr->zstream.avail_in); + + /* finish the compression */ + do + { + /* tell zlib we are finished */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + + if (ret == Z_OK) + { + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* check to make sure our output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + /* This could be optimized to realloc() */ + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof(png_charp))); + png_memcpy(comp->output_ptr, old_ptr, + old_max * png_sizeof(png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof(png_charp))); + } + + /* save off the data */ + comp->output_ptr[comp->num_output_ptr] = + (png_charp)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer pointers */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + } + else if (ret != Z_STREAM_END) + { + /* we got an error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* text length is number of buffers plus last buffer */ + text_len = png_ptr->zbuf_size * comp->num_output_ptr; + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out; + + return((int)text_len); +} + +/* ship the compressed text out via chunk writes */ +static void /* PRIVATE */ +png_write_compressed_data_out(png_structp png_ptr, compression_state *comp) +{ + int i; + + /* handle the no-compression case */ + if (comp->input) + { + png_write_chunk_data(png_ptr, (png_bytep)comp->input, + (png_size_t)comp->input_len); + return; + } + + /* write saved output buffers, if any */ + for (i = 0; i < comp->num_output_ptr; i++) + { + png_write_chunk_data(png_ptr, (png_bytep)comp->output_ptr[i], + (png_size_t)png_ptr->zbuf_size); + png_free(png_ptr, comp->output_ptr[i]); + comp->output_ptr[i]=NULL; + } + if (comp->max_output_ptr != 0) + png_free(png_ptr, comp->output_ptr); + comp->output_ptr=NULL; + /* write anything left in zbuf */ + if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size) + png_write_chunk_data(png_ptr, png_ptr->zbuf, + (png_size_t)(png_ptr->zbuf_size - png_ptr->zstream.avail_out)); + + /* reset zlib for another zTXt/iTXt or image data */ + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} +#endif + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. Note that the rest of this code depends upon this + * information being correct. + */ +void /* PRIVATE */ +png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, + int bit_depth, int color_type, int compression_type, int filter_type, + int interlace_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; +#endif + int ret; + + png_byte buf[13]; /* buffer to store the IHDR info */ + + png_debug(1, "in png_write_IHDR"); + /* Check that we have valid input data from the application info */ + switch (color_type) + { + case PNG_COLOR_TYPE_GRAY: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: + case 16: png_ptr->channels = 1; break; + default: png_error(png_ptr, "Invalid bit depth for grayscale image"); + } + break; + case PNG_COLOR_TYPE_RGB: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGB image"); + png_ptr->channels = 3; + break; + case PNG_COLOR_TYPE_PALETTE: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: png_ptr->channels = 1; break; + default: png_error(png_ptr, "Invalid bit depth for paletted image"); + } + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for grayscale+alpha image"); + png_ptr->channels = 2; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGBA image"); + png_ptr->channels = 4; + break; + default: + png_error(png_ptr, "Invalid image color type specified"); + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Invalid compression type specified"); + compression_type = PNG_COMPRESSION_TYPE_BASE; + } + + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && +#endif + filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Invalid filter type specified"); + filter_type = PNG_FILTER_TYPE_BASE; + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (interlace_type != PNG_INTERLACE_NONE && + interlace_type != PNG_INTERLACE_ADAM7) + { + png_warning(png_ptr, "Invalid interlace type specified"); + interlace_type = PNG_INTERLACE_ADAM7; + } +#else + interlace_type=PNG_INTERLACE_NONE; +#endif + + /* save off the relevent information */ + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->color_type = (png_byte)color_type; + png_ptr->interlaced = (png_byte)interlace_type; +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + png_ptr->width = width; + png_ptr->height = height; + + png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); + /* set the usr info, so any transformations can modify it */ + png_ptr->usr_width = png_ptr->width; + png_ptr->usr_bit_depth = png_ptr->bit_depth; + png_ptr->usr_channels = png_ptr->channels; + + /* pack the header information into the buffer */ + png_save_uint_32(buf, width); + png_save_uint_32(buf + 4, height); + buf[8] = (png_byte)bit_depth; + buf[9] = (png_byte)color_type; + buf[10] = (png_byte)compression_type; + buf[11] = (png_byte)filter_type; + buf[12] = (png_byte)interlace_type; + + /* write the chunk */ + png_write_chunk(png_ptr, (png_bytep)png_IHDR, buf, (png_size_t)13); + + /* initialize zlib with PNG info */ + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + if (!(png_ptr->do_filter)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + png_ptr->bit_depth < 8) + png_ptr->do_filter = PNG_FILTER_NONE; + else + png_ptr->do_filter = PNG_ALL_FILTERS; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)) + { + if (png_ptr->do_filter != PNG_FILTER_NONE) + png_ptr->zlib_strategy = Z_FILTERED; + else + png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL)) + png_ptr->zlib_level = Z_DEFAULT_COMPRESSION; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL)) + png_ptr->zlib_mem_level = 8; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS)) + png_ptr->zlib_window_bits = 15; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD)) + png_ptr->zlib_method = 8; + ret = deflateInit2(&png_ptr->zstream, png_ptr->zlib_level, + png_ptr->zlib_method, png_ptr->zlib_window_bits, + png_ptr->zlib_mem_level, png_ptr->zlib_strategy); + if (ret != Z_OK) + { + if (ret == Z_VERSION_ERROR) png_error(png_ptr, + "zlib failed to initialize compressor -- version error"); + if (ret == Z_STREAM_ERROR) png_error(png_ptr, + "zlib failed to initialize compressor -- stream error"); + if (ret == Z_MEM_ERROR) png_error(png_ptr, + "zlib failed to initialize compressor -- mem error"); + png_error(png_ptr, "zlib failed to initialize compressor"); + } + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + /* libpng is not interested in zstream.data_type */ + /* set it to a predefined value, to avoid its evaluation inside zlib */ + png_ptr->zstream.data_type = Z_BINARY; + + png_ptr->mode = PNG_HAVE_IHDR; +} + +/* write the palette. We are careful not to trust png_color to be in the + * correct order for PNG, so people can redefine it to any convenient + * structure. + */ +void /* PRIVATE */ +png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_PLTE; +#endif + png_uint_32 i; + png_colorp pal_ptr; + png_byte buf[3]; + + png_debug(1, "in png_write_PLTE"); + if (( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) && +#endif + num_pal == 0) || num_pal > 256) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_error(png_ptr, "Invalid number of colors in palette"); + } + else + { + png_warning(png_ptr, "Invalid number of colors in palette"); + return; + } + } + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring request to write a PLTE chunk in grayscale PNG"); + return; + } + + png_ptr->num_palette = (png_uint_16)num_pal; + png_debug1(3, "num_palette = %d", png_ptr->num_palette); + + png_write_chunk_start(png_ptr, (png_bytep)png_PLTE, + (png_uint_32)(num_pal * 3)); +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++) + { + buf[0] = pal_ptr->red; + buf[1] = pal_ptr->green; + buf[2] = pal_ptr->blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#else + /* This is a little slower but some buggy compilers need to do this instead */ + pal_ptr=palette; + for (i = 0; i < num_pal; i++) + { + buf[0] = pal_ptr[i].red; + buf[1] = pal_ptr[i].green; + buf[2] = pal_ptr[i].blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#endif + png_write_chunk_end(png_ptr); + png_ptr->mode |= PNG_HAVE_PLTE; +} + +/* write an IDAT chunk */ +void /* PRIVATE */ +png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; +#endif + png_debug(1, "in png_write_IDAT"); + + /* Optimize the CMF field in the zlib stream. */ + /* This hack of the zlib stream is compliant to the stream specification. */ + if (!(png_ptr->mode & PNG_HAVE_IDAT) && + png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + { + unsigned int z_cmf = data[0]; /* zlib compression method and flags */ + if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) + { + /* Avoid memory underflows and multiplication overflows. */ + /* The conditions below are practically always satisfied; + however, they still must be checked. */ + if (length >= 2 && + png_ptr->height < 16384 && png_ptr->width < 16384) + { + png_uint_32 uncompressed_idat_size = png_ptr->height * + ((png_ptr->width * + png_ptr->channels * png_ptr->bit_depth + 15) >> 3); + unsigned int z_cinfo = z_cmf >> 4; + unsigned int half_z_window_size = 1 << (z_cinfo + 7); + while (uncompressed_idat_size <= half_z_window_size && + half_z_window_size >= 256) + { + z_cinfo--; + half_z_window_size >>= 1; + } + z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); + if (data[0] != (png_byte)z_cmf) + { + data[0] = (png_byte)z_cmf; + data[1] &= 0xe0; + data[1] += (png_byte)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f); + } + } + } + else + png_error(png_ptr, + "Invalid zlib compression method or flags in IDAT"); + } + + png_write_chunk(png_ptr, (png_bytep)png_IDAT, data, length); + png_ptr->mode |= PNG_HAVE_IDAT; +} + +/* write an IEND chunk */ +void /* PRIVATE */ +png_write_IEND(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IEND; +#endif + png_debug(1, "in png_write_IEND"); + png_write_chunk(png_ptr, (png_bytep)png_IEND, png_bytep_NULL, + (png_size_t)0); + png_ptr->mode |= PNG_HAVE_IEND; +} + +#if defined(PNG_WRITE_gAMA_SUPPORTED) +/* write a gAMA chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA(png_structp png_ptr, double file_gamma) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_gAMA; +#endif + png_uint_32 igamma; + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA"); + /* file_gamma is saved in 1/100,000ths */ + igamma = (png_uint_32)(file_gamma * 100000.0 + 0.5); + png_save_uint_32(buf, igamma); + png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_gAMA; +#endif + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA"); + /* file_gamma is saved in 1/100,000ths */ + png_save_uint_32(buf, (png_uint_32)file_gamma); + png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4); +} +#endif +#endif + +#if defined(PNG_WRITE_sRGB_SUPPORTED) +/* write a sRGB chunk */ +void /* PRIVATE */ +png_write_sRGB(png_structp png_ptr, int srgb_intent) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sRGB; +#endif + png_byte buf[1]; + + png_debug(1, "in png_write_sRGB"); + if (srgb_intent >= PNG_sRGB_INTENT_LAST) + png_warning(png_ptr, + "Invalid sRGB rendering intent specified"); + buf[0]=(png_byte)srgb_intent; + png_write_chunk(png_ptr, (png_bytep)png_sRGB, buf, (png_size_t)1); +} +#endif + +#if defined(PNG_WRITE_iCCP_SUPPORTED) +/* write an iCCP chunk */ +void /* PRIVATE */ +png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type, + png_charp profile, int profile_len) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_iCCP; +#endif + png_size_t name_len; + png_charp new_name; + compression_state comp; + int embedded_profile_len = 0; + + png_debug(1, "in png_write_iCCP"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + comp.input_len = 0; + + if ((name_len = png_check_keyword(png_ptr, name, + &new_name)) == 0) + return; + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_warning(png_ptr, "Unknown compression type in iCCP chunk"); + + if (profile == NULL) + profile_len = 0; + + if (profile_len > 3) + embedded_profile_len = + ((*( (png_bytep)profile ))<<24) | + ((*( (png_bytep)profile + 1))<<16) | + ((*( (png_bytep)profile + 2))<< 8) | + ((*( (png_bytep)profile + 3)) ); + + if (profile_len < embedded_profile_len) + { + png_warning(png_ptr, + "Embedded profile length too large in iCCP chunk"); + return; + } + + if (profile_len > embedded_profile_len) + { + png_warning(png_ptr, + "Truncating profile to actual length in iCCP chunk"); + profile_len = embedded_profile_len; + } + + if (profile_len) + profile_len = png_text_compress(png_ptr, profile, + (png_size_t)profile_len, PNG_COMPRESSION_TYPE_BASE, &comp); + + /* make sure we include the NULL after the name and the compression type */ + png_write_chunk_start(png_ptr, (png_bytep)png_iCCP, + (png_uint_32)(name_len + profile_len + 2)); + new_name[name_len + 1] = 0x00; + png_write_chunk_data(png_ptr, (png_bytep)new_name, + (png_size_t)(name_len + 2)); + + if (profile_len) + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#if defined(PNG_WRITE_sPLT_SUPPORTED) +/* write a sPLT chunk */ +void /* PRIVATE */ +png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sPLT; +#endif + png_size_t name_len; + png_charp new_name; + png_byte entrybuf[10]; + int entry_size = (spalette->depth == 8 ? 6 : 10); + int palette_size = entry_size * spalette->nentries; + png_sPLT_entryp ep; +#ifdef PNG_NO_POINTER_INDEXING + int i; +#endif + + png_debug(1, "in png_write_sPLT"); + if ((name_len = png_check_keyword(png_ptr, + spalette->name, &new_name))==0) + return; + + /* make sure we include the NULL after the name */ + png_write_chunk_start(png_ptr, (png_bytep)png_sPLT, + (png_uint_32)(name_len + 2 + palette_size)); + png_write_chunk_data(png_ptr, (png_bytep)new_name, + (png_size_t)(name_len + 1)); + png_write_chunk_data(png_ptr, (png_bytep)&spalette->depth, (png_size_t)1); + + /* loop through each palette entry, writing appropriately */ +#ifndef PNG_NO_POINTER_INDEXING + for (ep = spalette->entries; epentries + spalette->nentries; ep++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep->red; + entrybuf[1] = (png_byte)ep->green; + entrybuf[2] = (png_byte)ep->blue; + entrybuf[3] = (png_byte)ep->alpha; + png_save_uint_16(entrybuf + 4, ep->frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep->red); + png_save_uint_16(entrybuf + 2, ep->green); + png_save_uint_16(entrybuf + 4, ep->blue); + png_save_uint_16(entrybuf + 6, ep->alpha); + png_save_uint_16(entrybuf + 8, ep->frequency); + } + png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); + } +#else + ep=spalette->entries; + for (i=0; i>spalette->nentries; i++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep[i].red; + entrybuf[1] = (png_byte)ep[i].green; + entrybuf[2] = (png_byte)ep[i].blue; + entrybuf[3] = (png_byte)ep[i].alpha; + png_save_uint_16(entrybuf + 4, ep[i].frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep[i].red); + png_save_uint_16(entrybuf + 2, ep[i].green); + png_save_uint_16(entrybuf + 4, ep[i].blue); + png_save_uint_16(entrybuf + 6, ep[i].alpha); + png_save_uint_16(entrybuf + 8, ep[i].frequency); + } + png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); + } +#endif + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#if defined(PNG_WRITE_sBIT_SUPPORTED) +/* write the sBIT chunk */ +void /* PRIVATE */ +png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sBIT; +#endif + png_byte buf[4]; + png_size_t size; + + png_debug(1, "in png_write_sBIT"); + /* make sure we don't depend upon the order of PNG_COLOR_8 */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + png_byte maxbits; + + maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 : + png_ptr->usr_bit_depth); + if (sbit->red == 0 || sbit->red > maxbits || + sbit->green == 0 || sbit->green > maxbits || + sbit->blue == 0 || sbit->blue > maxbits) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->red; + buf[1] = sbit->green; + buf[2] = sbit->blue; + size = 3; + } + else + { + if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->gray; + size = 1; + } + + if (color_type & PNG_COLOR_MASK_ALPHA) + { + if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[size++] = sbit->alpha; + } + + png_write_chunk(png_ptr, (png_bytep)png_sBIT, buf, size); +} +#endif + +#if defined(PNG_WRITE_cHRM_SUPPORTED) +/* write the cHRM chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM(png_structp png_ptr, double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_cHRM; +#endif + png_byte buf[32]; + + png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, + int_green_x, int_green_y, int_blue_x, int_blue_y; + + png_debug(1, "in png_write_cHRM"); + + int_white_x = (png_uint_32)(white_x * 100000.0 + 0.5); + int_white_y = (png_uint_32)(white_y * 100000.0 + 0.5); + int_red_x = (png_uint_32)(red_x * 100000.0 + 0.5); + int_red_y = (png_uint_32)(red_y * 100000.0 + 0.5); + int_green_x = (png_uint_32)(green_x * 100000.0 + 0.5); + int_green_y = (png_uint_32)(green_y * 100000.0 + 0.5); + int_blue_x = (png_uint_32)(blue_x * 100000.0 + 0.5); + int_blue_y = (png_uint_32)(blue_y * 100000.0 + 0.5); + +#if !defined(PNG_NO_CHECK_cHRM) + if (png_check_cHRM_fixed(png_ptr, int_white_x, int_white_y, + int_red_x, int_red_y, int_green_x, int_green_y, int_blue_x, int_blue_y)) +#endif + { + /* each value is saved in 1/100,000ths */ + + png_save_uint_32(buf, int_white_x); + png_save_uint_32(buf + 4, int_white_y); + + png_save_uint_32(buf + 8, int_red_x); + png_save_uint_32(buf + 12, int_red_y); + + png_save_uint_32(buf + 16, int_green_x); + png_save_uint_32(buf + 20, int_green_y); + + png_save_uint_32(buf + 24, int_blue_x); + png_save_uint_32(buf + 28, int_blue_y); + + png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32); + } +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x, + png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y, + png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, + png_fixed_point blue_y) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_cHRM; +#endif + png_byte buf[32]; + + png_debug(1, "in png_write_cHRM"); + /* each value is saved in 1/100,000ths */ +#if !defined(PNG_NO_CHECK_cHRM) + if (png_check_cHRM_fixed(png_ptr, white_x, white_y, red_x, red_y, + green_x, green_y, blue_x, blue_y)) +#endif + { + png_save_uint_32(buf, (png_uint_32)white_x); + png_save_uint_32(buf + 4, (png_uint_32)white_y); + + png_save_uint_32(buf + 8, (png_uint_32)red_x); + png_save_uint_32(buf + 12, (png_uint_32)red_y); + + png_save_uint_32(buf + 16, (png_uint_32)green_x); + png_save_uint_32(buf + 20, (png_uint_32)green_y); + + png_save_uint_32(buf + 24, (png_uint_32)blue_x); + png_save_uint_32(buf + 28, (png_uint_32)blue_y); + + png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32); + } +} +#endif +#endif + +#if defined(PNG_WRITE_tRNS_SUPPORTED) +/* write the tRNS chunk */ +void /* PRIVATE */ +png_write_tRNS(png_structp png_ptr, png_bytep trans, png_color_16p tran, + int num_trans, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tRNS; +#endif + png_byte buf[6]; + + png_debug(1, "in png_write_tRNS"); + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette) + { + png_warning(png_ptr, "Invalid number of transparent colors specified"); + return; + } + /* write the chunk out as it is */ + png_write_chunk(png_ptr, (png_bytep)png_tRNS, trans, + (png_size_t)num_trans); + } + else if (color_type == PNG_COLOR_TYPE_GRAY) + { + /* one 16 bit value */ + if (tran->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, tran->gray); + png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)2); + } + else if (color_type == PNG_COLOR_TYPE_RGB) + { + /* three 16 bit values */ + png_save_uint_16(buf, tran->red); + png_save_uint_16(buf + 2, tran->green); + png_save_uint_16(buf + 4, tran->blue); + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)6); + } + else + { + png_warning(png_ptr, "Can't write tRNS with an alpha channel"); + } +} +#endif + +#if defined(PNG_WRITE_bKGD_SUPPORTED) +/* write the background chunk */ +void /* PRIVATE */ +png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_bKGD; +#endif + png_byte buf[6]; + + png_debug(1, "in png_write_bKGD"); + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if ( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + (png_ptr->num_palette || + (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) && +#endif + back->index >= png_ptr->num_palette) + { + png_warning(png_ptr, "Invalid background palette index"); + return; + } + buf[0] = back->index; + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)1); + } + else if (color_type & PNG_COLOR_MASK_COLOR) + { + png_save_uint_16(buf, back->red); + png_save_uint_16(buf + 2, back->green); + png_save_uint_16(buf + 4, back->blue); + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)6); + } + else + { + if (back->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, back->gray); + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)2); + } +} +#endif + +#if defined(PNG_WRITE_hIST_SUPPORTED) +/* write the histogram */ +void /* PRIVATE */ +png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_hIST; +#endif + int i; + png_byte buf[3]; + + png_debug(1, "in png_write_hIST"); + if (num_hist > (int)png_ptr->num_palette) + { + png_debug2(3, "num_hist = %d, num_palette = %d", num_hist, + png_ptr->num_palette); + png_warning(png_ptr, "Invalid number of histogram entries specified"); + return; + } + + png_write_chunk_start(png_ptr, (png_bytep)png_hIST, + (png_uint_32)(num_hist * 2)); + for (i = 0; i < num_hist; i++) + { + png_save_uint_16(buf, hist[i]); + png_write_chunk_data(png_ptr, buf, (png_size_t)2); + } + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification, + * and if invalid, correct the keyword rather than discarding the entire + * chunk. The PNG 1.0 specification requires keywords 1-79 characters in + * length, forbids leading or trailing whitespace, multiple internal spaces, + * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. + * + * The new_key is allocated to hold the corrected keyword and must be freed + * by the calling routine. This avoids problems with trying to write to + * static keywords without having to have duplicate copies of the strings. + */ +png_size_t /* PRIVATE */ +png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) +{ + png_size_t key_len; + png_charp kp, dp; + int kflag; + int kwarn=0; + + png_debug(1, "in png_check_keyword"); + *new_key = NULL; + + if (key == NULL || (key_len = png_strlen(key)) == 0) + { + png_warning(png_ptr, "zero length keyword"); + return ((png_size_t)0); + } + + png_debug1(2, "Keyword to be checked is '%s'", key); + + *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2)); + if (*new_key == NULL) + { + png_warning(png_ptr, "Out of memory while procesing keyword"); + return ((png_size_t)0); + } + + /* Replace non-printing characters with a blank and print a warning */ + for (kp = key, dp = *new_key; *kp != '\0'; kp++, dp++) + { + if ((png_byte)*kp < 0x20 || + ((png_byte)*kp > 0x7E && (png_byte)*kp < 0xA1)) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[40]; + + png_snprintf(msg, 40, + "invalid keyword character 0x%02X", (png_byte)*kp); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "invalid character in keyword"); +#endif + *dp = ' '; + } + else + { + *dp = *kp; + } + } + *dp = '\0'; + + /* Remove any trailing white space. */ + kp = *new_key + key_len - 1; + if (*kp == ' ') + { + png_warning(png_ptr, "trailing spaces removed from keyword"); + + while (*kp == ' ') + { + *(kp--) = '\0'; + key_len--; + } + } + + /* Remove any leading white space. */ + kp = *new_key; + if (*kp == ' ') + { + png_warning(png_ptr, "leading spaces removed from keyword"); + + while (*kp == ' ') + { + kp++; + key_len--; + } + } + + png_debug1(2, "Checking for multiple internal spaces in '%s'", kp); + + /* Remove multiple internal spaces. */ + for (kflag = 0, dp = *new_key; *kp != '\0'; kp++) + { + if (*kp == ' ' && kflag == 0) + { + *(dp++) = *kp; + kflag = 1; + } + else if (*kp == ' ') + { + key_len--; + kwarn=1; + } + else + { + *(dp++) = *kp; + kflag = 0; + } + } + *dp = '\0'; + if (kwarn) + png_warning(png_ptr, "extra interior spaces removed from keyword"); + + if (key_len == 0) + { + png_free(png_ptr, *new_key); + *new_key=NULL; + png_warning(png_ptr, "Zero length keyword"); + } + + if (key_len > 79) + { + png_warning(png_ptr, "keyword length must be 1 - 79 characters"); + (*new_key)[79] = '\0'; + key_len = 79; + } + + return (key_len); +} +#endif + +#if defined(PNG_WRITE_tEXt_SUPPORTED) +/* write a tEXt chunk */ +void /* PRIVATE */ +png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tEXt; +#endif + png_size_t key_len; + png_charp new_key; + + png_debug(1, "in png_write_tEXt"); + if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) + return; + + if (text == NULL || *text == '\0') + text_len = 0; + else + text_len = png_strlen(text); + + /* make sure we include the 0 after the key */ + png_write_chunk_start(png_ptr, (png_bytep)png_tEXt, + (png_uint_32)(key_len + text_len + 1)); + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, + (png_size_t)(key_len + 1)); + if (text_len) + png_write_chunk_data(png_ptr, (png_bytep)text, (png_size_t)text_len); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); +} +#endif + +#if defined(PNG_WRITE_zTXt_SUPPORTED) +/* write a compressed text chunk */ +void /* PRIVATE */ +png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len, int compression) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_zTXt; +#endif + png_size_t key_len; + char buf[1]; + png_charp new_key; + compression_state comp; + + png_debug(1, "in png_write_zTXt"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + comp.input_len = 0; + + if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_free(png_ptr, new_key); + return; + } + + if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE) + { + png_write_tEXt(png_ptr, new_key, text, (png_size_t)0); + png_free(png_ptr, new_key); + return; + } + + text_len = png_strlen(text); + + /* compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression, + &comp); + + /* write start of chunk */ + png_write_chunk_start(png_ptr, (png_bytep)png_zTXt, + (png_uint_32)(key_len+text_len + 2)); + /* write key */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, + (png_size_t)(key_len + 1)); + png_free(png_ptr, new_key); + + buf[0] = (png_byte)compression; + /* write compression */ + png_write_chunk_data(png_ptr, (png_bytep)buf, (png_size_t)1); + /* write the compressed data */ + png_write_compressed_data_out(png_ptr, &comp); + + /* close the chunk */ + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_iTXt_SUPPORTED) +/* write an iTXt chunk */ +void /* PRIVATE */ +png_write_iTXt(png_structp png_ptr, int compression, png_charp key, + png_charp lang, png_charp lang_key, png_charp text) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_iTXt; +#endif + png_size_t lang_len, key_len, lang_key_len, text_len; + png_charp new_lang; + png_charp new_key = NULL; + png_byte cbuf[2]; + compression_state comp; + + png_debug(1, "in png_write_iTXt"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + + if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) + return; + + if ((lang_len = png_check_keyword(png_ptr, lang, &new_lang))==0) + { + png_warning(png_ptr, "Empty language field in iTXt chunk"); + new_lang = NULL; + lang_len = 0; + } + + if (lang_key == NULL) + lang_key_len = 0; + else + lang_key_len = png_strlen(lang_key); + + if (text == NULL) + text_len = 0; + else + text_len = png_strlen(text); + + /* compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression-2, + &comp); + + + /* make sure we include the compression flag, the compression byte, + * and the NULs after the key, lang, and lang_key parts */ + + png_write_chunk_start(png_ptr, (png_bytep)png_iTXt, + (png_uint_32)( + 5 /* comp byte, comp flag, terminators for key, lang and lang_key */ + + key_len + + lang_len + + lang_key_len + + text_len)); + + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, + (png_size_t)(key_len + 1)); + + /* set the compression flag */ + if (compression == PNG_ITXT_COMPRESSION_NONE || \ + compression == PNG_TEXT_COMPRESSION_NONE) + cbuf[0] = 0; + else /* compression == PNG_ITXT_COMPRESSION_zTXt */ + cbuf[0] = 1; + /* set the compression method */ + cbuf[1] = 0; + png_write_chunk_data(png_ptr, cbuf, (png_size_t)2); + + cbuf[0] = 0; + png_write_chunk_data(png_ptr, (new_lang ? (png_bytep)new_lang : cbuf), + (png_size_t)(lang_len + 1)); + png_write_chunk_data(png_ptr, (lang_key ? (png_bytep)lang_key : cbuf), + (png_size_t)(lang_key_len + 1)); + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); + png_free(png_ptr, new_lang); +} +#endif + +#if defined(PNG_WRITE_oFFs_SUPPORTED) +/* write the oFFs chunk */ +void /* PRIVATE */ +png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset, + int unit_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_oFFs; +#endif + png_byte buf[9]; + + png_debug(1, "in png_write_oFFs"); + if (unit_type >= PNG_OFFSET_LAST) + png_warning(png_ptr, "Unrecognized unit type for oFFs chunk"); + + png_save_int_32(buf, x_offset); + png_save_int_32(buf + 4, y_offset); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, (png_bytep)png_oFFs, buf, (png_size_t)9); +} +#endif +#if defined(PNG_WRITE_pCAL_SUPPORTED) +/* write the pCAL chunk (described in the PNG extensions document) */ +void /* PRIVATE */ +png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, + png_int_32 X1, int type, int nparams, png_charp units, png_charpp params) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_pCAL; +#endif + png_size_t purpose_len, units_len, total_len; + png_uint_32p params_len; + png_byte buf[10]; + png_charp new_purpose; + int i; + + png_debug1(1, "in png_write_pCAL (%d parameters)", nparams); + if (type >= PNG_EQUATION_LAST) + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + + purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1; + png_debug1(3, "pCAL purpose length = %d", (int)purpose_len); + units_len = png_strlen(units) + (nparams == 0 ? 0 : 1); + png_debug1(3, "pCAL units length = %d", (int)units_len); + total_len = purpose_len + units_len + 10; + + params_len = (png_uint_32p)png_malloc(png_ptr, + (png_uint_32)(nparams * png_sizeof(png_uint_32))); + + /* Find the length of each parameter, making sure we don't count the + null terminator for the last parameter. */ + for (i = 0; i < nparams; i++) + { + params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1); + png_debug2(3, "pCAL parameter %d length = %lu", i, + (unsigned long) params_len[i]); + total_len += (png_size_t)params_len[i]; + } + + png_debug1(3, "pCAL total length = %d", (int)total_len); + png_write_chunk_start(png_ptr, (png_bytep)png_pCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, (png_bytep)new_purpose, + (png_size_t)purpose_len); + png_save_int_32(buf, X0); + png_save_int_32(buf + 4, X1); + buf[8] = (png_byte)type; + buf[9] = (png_byte)nparams; + png_write_chunk_data(png_ptr, buf, (png_size_t)10); + png_write_chunk_data(png_ptr, (png_bytep)units, (png_size_t)units_len); + + png_free(png_ptr, new_purpose); + + for (i = 0; i < nparams; i++) + { + png_write_chunk_data(png_ptr, (png_bytep)params[i], + (png_size_t)params_len[i]); + } + + png_free(png_ptr, params_len); + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_sCAL_SUPPORTED) +/* write the sCAL chunk */ +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) +void /* PRIVATE */ +png_write_sCAL(png_structp png_ptr, int unit, double width, double height) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sCAL; +#endif + char buf[64]; + png_size_t total_len; + + png_debug(1, "in png_write_sCAL"); + + buf[0] = (char)unit; +#if defined(_WIN32_WCE) +/* sprintf() function is not supported on WindowsCE */ + { + wchar_t wc_buf[32]; + size_t wc_len; + swprintf(wc_buf, TEXT("%12.12e"), width); + wc_len = wcslen(wc_buf); + WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + 1, wc_len, NULL, NULL); + total_len = wc_len + 2; + swprintf(wc_buf, TEXT("%12.12e"), height); + wc_len = wcslen(wc_buf); + WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + total_len, wc_len, + NULL, NULL); + total_len += wc_len; + } +#else + png_snprintf(buf + 1, 63, "%12.12e", width); + total_len = 1 + png_strlen(buf + 1) + 1; + png_snprintf(buf + total_len, 64-total_len, "%12.12e", height); + total_len += png_strlen(buf + total_len); +#endif + + png_debug1(3, "sCAL total length = %u", (unsigned int)total_len); + png_write_chunk(png_ptr, (png_bytep)png_sCAL, (png_bytep)buf, total_len); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width, + png_charp height) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sCAL; +#endif + png_byte buf[64]; + png_size_t wlen, hlen, total_len; + + png_debug(1, "in png_write_sCAL_s"); + + wlen = png_strlen(width); + hlen = png_strlen(height); + total_len = wlen + hlen + 2; + if (total_len > 64) + { + png_warning(png_ptr, "Can't write sCAL (buffer too small)"); + return; + } + + buf[0] = (png_byte)unit; + png_memcpy(buf + 1, width, wlen + 1); /* append the '\0' here */ + png_memcpy(buf + wlen + 2, height, hlen); /* do NOT append the '\0' here */ + + png_debug1(3, "sCAL total length = %u", (unsigned int)total_len); + png_write_chunk(png_ptr, (png_bytep)png_sCAL, buf, total_len); +} +#endif +#endif +#endif + +#if defined(PNG_WRITE_pHYs_SUPPORTED) +/* write the pHYs chunk */ +void /* PRIVATE */ +png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit, + png_uint_32 y_pixels_per_unit, + int unit_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_pHYs; +#endif + png_byte buf[9]; + + png_debug(1, "in png_write_pHYs"); + if (unit_type >= PNG_RESOLUTION_LAST) + png_warning(png_ptr, "Unrecognized unit type for pHYs chunk"); + + png_save_uint_32(buf, x_pixels_per_unit); + png_save_uint_32(buf + 4, y_pixels_per_unit); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, (png_bytep)png_pHYs, buf, (png_size_t)9); +} +#endif + +#if defined(PNG_WRITE_tIME_SUPPORTED) +/* Write the tIME chunk. Use either png_convert_from_struct_tm() + * or png_convert_from_time_t(), or fill in the structure yourself. + */ +void /* PRIVATE */ +png_write_tIME(png_structp png_ptr, png_timep mod_time) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tIME; +#endif + png_byte buf[7]; + + png_debug(1, "in png_write_tIME"); + if (mod_time->month > 12 || mod_time->month < 1 || + mod_time->day > 31 || mod_time->day < 1 || + mod_time->hour > 23 || mod_time->second > 60) + { + png_warning(png_ptr, "Invalid time specified for tIME chunk"); + return; + } + + png_save_uint_16(buf, mod_time->year); + buf[2] = mod_time->month; + buf[3] = mod_time->day; + buf[4] = mod_time->hour; + buf[5] = mod_time->minute; + buf[6] = mod_time->second; + + png_write_chunk(png_ptr, (png_bytep)png_tIME, buf, (png_size_t)7); +} +#endif + +/* initializes the row writing capability of libpng */ +void /* PRIVATE */ +png_write_start_row(png_structp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif +#endif + + png_size_t buf_size; + + png_debug(1, "in png_write_start_row"); + buf_size = (png_size_t)(PNG_ROWBYTES( + png_ptr->usr_channels*png_ptr->usr_bit_depth, png_ptr->width) + 1); + + /* set up row buffer */ + png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)buf_size); + png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; + +#ifndef PNG_NO_WRITE_FILTER + /* set up filtering buffer, if using this filter */ + if (png_ptr->do_filter & PNG_FILTER_SUB) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + /* We only need to keep the previous row if we are using one of these. */ + if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) + { + /* set up previous row buffer */ + png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, + (png_uint_32)buf_size); + png_memset(png_ptr->prev_row, 0, buf_size); + + if (png_ptr->do_filter & PNG_FILTER_UP) + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + + if (png_ptr->do_filter & PNG_FILTER_AVG) + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + + if (png_ptr->do_filter & PNG_FILTER_PAETH) + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } +#endif /* PNG_NO_WRITE_FILTER */ + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* if interlaced, we need to set up width and height of pass */ + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 - + png_pass_start[0]) / png_pass_inc[0]; + } + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + } + else +#endif + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; +} + +/* Internal use only. Called when finished processing a row of data. */ +void /* PRIVATE */ +png_write_finish_row(png_structp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif +#endif + + int ret; + + png_debug(1, "in png_write_finish_row"); + /* next row */ + png_ptr->row_number++; + + /* see if we are done */ + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* if interlaced, go to next pass */ + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + if (png_ptr->transformations & PNG_INTERLACE) + { + png_ptr->pass++; + } + else + { + /* loop until we find a non-zero width or height pass */ + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->usr_width = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (png_ptr->transformations & PNG_INTERLACE) + break; + } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); + + } + + /* reset the row above the image for the next pass */ + if (png_ptr->pass < 7) + { + if (png_ptr->prev_row != NULL) + png_memset(png_ptr->prev_row, 0, + (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* + png_ptr->usr_bit_depth, png_ptr->width)) + 1); + return; + } + } +#endif + + /* if we get here, we've just written the last row, so we need + to flush the compressor */ + do + { + /* tell the compressor we are done */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + /* check for an error */ + if (ret == Z_OK) + { + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + else if (ret != Z_STREAM_END) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* write any extra space */ + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size - + png_ptr->zstream.avail_out); + } + + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Pick out the correct pixels for the interlace pass. + * The basic idea here is to go through the row with a source + * pointer and a destination pointer (sp and dp), and copy the + * correct pixels for the pass. As the row gets compacted, + * sp will always be >= dp, so we should never overwrite anything. + * See the default: case for the easiest code to understand. + */ +void /* PRIVATE */ +png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +#endif + + png_debug(1, "in png_do_write_interlace"); + /* we don't have to do anything on the last pass (6) */ +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && pass < 6) +#else + if (pass < 6) +#endif + { + /* each pixel depth is handled separately */ + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + d = 0; + shift = 7; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 3); + value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01; + d |= (value << shift); + + if (shift == 0) + { + shift = 7; + *dp++ = (png_byte)d; + d = 0; + } + else + shift--; + + } + if (shift != 7) + *dp = (png_byte)d; + break; + } + case 2: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 6; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 2); + value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03; + d |= (value << shift); + + if (shift == 0) + { + shift = 6; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 2; + } + if (shift != 6) + *dp = (png_byte)d; + break; + } + case 4: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 4; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 1); + value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; + d |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 4; + } + if (shift != 4) + *dp = (png_byte)d; + break; + } + default: + { + png_bytep sp; + png_bytep dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + png_size_t pixel_bytes; + + /* start at the beginning */ + dp = row; + /* find out how many bytes each pixel takes up */ + pixel_bytes = (row_info->pixel_depth >> 3); + /* loop through the row, only looking at the pixels that + matter */ + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + /* find out where the original pixel is */ + sp = row + (png_size_t)i * pixel_bytes; + /* move the pixel */ + if (dp != sp) + png_memcpy(dp, sp, pixel_bytes); + /* next pixel */ + dp += pixel_bytes; + } + break; + } + } + /* set new row width */ + row_info->width = (row_info->width + + png_pass_inc[pass] - 1 - + png_pass_start[pass]) / + png_pass_inc[pass]; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +/* This filters the row, chooses which filter to use, if it has not already + * been specified by the application, and then writes the row out with the + * chosen filter. + */ +#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1) +#define PNG_HISHIFT 10 +#define PNG_LOMASK ((png_uint_32)0xffffL) +#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT)) +void /* PRIVATE */ +png_write_find_filter(png_structp png_ptr, png_row_infop row_info) +{ + png_bytep best_row; +#ifndef PNG_NO_WRITE_FILTER + png_bytep prev_row, row_buf; + png_uint_32 mins, bpp; + png_byte filter_to_do = png_ptr->do_filter; + png_uint_32 row_bytes = row_info->rowbytes; +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + int num_p_filters = (int)png_ptr->num_prev_filters; +#endif + + png_debug(1, "in png_write_find_filter"); + /* find out how many bytes offset each pixel is */ + bpp = (row_info->pixel_depth + 7) >> 3; + + prev_row = png_ptr->prev_row; +#endif + best_row = png_ptr->row_buf; +#ifndef PNG_NO_WRITE_FILTER + row_buf = best_row; + mins = PNG_MAXSUM; + + /* The prediction method we use is to find which method provides the + * smallest value when summing the absolute values of the distances + * from zero, using anything >= 128 as negative numbers. This is known + * as the "minimum sum of absolute differences" heuristic. Other + * heuristics are the "weighted minimum sum of absolute differences" + * (experimental and can in theory improve compression), and the "zlib + * predictive" method (not implemented yet), which does test compressions + * of lines using different filter methods, and then chooses the + * (series of) filter(s) that give minimum compressed data size (VERY + * computationally expensive). + * + * GRR 980525: consider also + * (1) minimum sum of absolute differences from running average (i.e., + * keep running sum of non-absolute differences & count of bytes) + * [track dispersion, too? restart average if dispersion too large?] + * (1b) minimum sum of absolute differences from sliding average, probably + * with window size <= deflate window (usually 32K) + * (2) minimum sum of squared differences from zero or running average + * (i.e., ~ root-mean-square approach) + */ + + + /* We don't need to test the 'no filter' case if this is the only filter + * that has been chosen, as it doesn't actually do anything to the data. + */ + if ((filter_to_do & PNG_FILTER_NONE) && + filter_to_do != PNG_FILTER_NONE) + { + png_bytep rp; + png_uint_32 sum = 0; + png_uint_32 i; + int v; + + for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) + { + v = *rp; + sum += (v < 128) ? v : 256 - v; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + png_uint_32 sumhi, sumlo; + int j; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */ + + /* Reduce the sum if we match any of the previous rows */ + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + /* Factor in the cost of this filter (this is here for completeness, + * but it makes no sense to have a "cost" for the NONE filter, as + * it has the minimum possible computational cost - none). + */ + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + mins = sum; + } + + /* sub filter */ + if (filter_to_do == PNG_FILTER_SUB) + /* it's the only filter so no testing is needed */ + { + png_bytep rp, lp, dp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + *dp = *rp; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + } + best_row = png_ptr->sub_row; + } + + else if (filter_to_do & PNG_FILTER_SUB) + { + png_bytep rp, dp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + /* We temporarily increase the "minimum sum" by the factor we + * would reduce the sum of this filter, so that we can do the + * early exit comparison without scaling the sum each time. + */ + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + v = *dp = *rp; + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->sub_row; + } + } + + /* up filter */ + if (filter_to_do == PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; + i++, rp++, pp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); + } + best_row = png_ptr->up_row; + } + + else if (filter_to_do & PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->up_row; + } + } + + /* avg filter */ + if (filter_to_do == PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) + & 0xff); + } + best_row = png_ptr->avg_row; + } + + else if (filter_to_do & PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + v = *dp++ = + (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->avg_row; + } + } + + /* Paeth filter */ + if (filter_to_do == PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + } + best_row = png_ptr->paeth_row; + } + + else if (filter_to_do & PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + +#ifndef PNG_SLOW_PAETH + p = b - c; + pc = a - c; +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; +#else /* PNG_SLOW_PAETH */ + p = a + b - c; + pa = abs(p - a); + pb = abs(p - b); + pc = abs(p - c); + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; +#endif /* PNG_SLOW_PAETH */ + + v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + best_row = png_ptr->paeth_row; + } + } +#endif /* PNG_NO_WRITE_FILTER */ + /* Do the actual writing of the filtered row data from the chosen filter. */ + + png_write_filtered_row(png_ptr, best_row); + +#ifndef PNG_NO_WRITE_FILTER +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + /* Save the type of filter we picked this time for future calculations */ + if (png_ptr->num_prev_filters > 0) + { + int j; + for (j = 1; j < num_p_filters; j++) + { + png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1]; + } + png_ptr->prev_filters[j] = best_row[0]; + } +#endif +#endif /* PNG_NO_WRITE_FILTER */ +} + + +/* Do the actual writing of a previously filtered row. */ +void /* PRIVATE */ +png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row) +{ + png_debug(1, "in png_write_filtered_row"); + png_debug1(2, "filter = %d", filtered_row[0]); + /* set up the zlib input buffer */ + + png_ptr->zstream.next_in = filtered_row; + png_ptr->zstream.avail_in = (uInt)png_ptr->row_info.rowbytes + 1; + /* repeat until we have compressed all the data */ + do + { + int ret; /* return of zlib */ + + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + /* check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + /* see if it is time to write another IDAT */ + if (!(png_ptr->zstream.avail_out)) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + /* repeat until all data has been compressed */ + } while (png_ptr->zstream.avail_in); + + /* swap the current and previous rows */ + if (png_ptr->prev_row != NULL) + { + png_bytep tptr; + + tptr = png_ptr->prev_row; + png_ptr->prev_row = png_ptr->row_buf; + png_ptr->row_buf = tptr; + } + + /* finish row - updates counters and flushes zlib if last row */ + png_write_finish_row(png_ptr); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_ptr->flush_rows++; + + if (png_ptr->flush_dist > 0 && + png_ptr->flush_rows >= png_ptr->flush_dist) + { + png_write_flush(png_ptr); + } +#endif +} +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/ext/libpng/projects/visualc71/PRJ0041.mak b/ext/libpng/projects/visualc71/PRJ0041.mak new file mode 100644 index 00000000..3a597b02 --- /dev/null +++ b/ext/libpng/projects/visualc71/PRJ0041.mak @@ -0,0 +1,21 @@ +# Prevent "Cannot find missing dependency..." warnings while compiling +# pngw32.rc (PRJ0041). + +all: $(IntDir)\alloc.h \ + $(IntDir)\fp.h \ + $(IntDir)\m68881.h \ + $(IntDir)\mem.h \ + $(IntDir)\pngusr.h \ + $(IntDir)\strings.h \ + $(IntDir)\unistd.h \ + $(IntDir)\unixio.h + +$(IntDir)\alloc.h \ +$(IntDir)\fp.h \ +$(IntDir)\m68881.h \ +$(IntDir)\mem.h \ +$(IntDir)\pngusr.h \ +$(IntDir)\strings.h \ +$(IntDir)\unistd.h \ +$(IntDir)\unixio.h: + @!echo.>$@ diff --git a/ext/libpng/projects/visualc71/libpng_2005Express.vcproj b/ext/libpng/projects/visualc71/libpng_2005Express.vcproj new file mode 100644 index 00000000..cc51e284 --- /dev/null +++ b/ext/libpng/projects/visualc71/libpng_2005Express.vcproj @@ -0,0 +1,507 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/libpng/projects/visualc71/libpng_VS2003.vcproj b/ext/libpng/projects/visualc71/libpng_VS2003.vcproj new file mode 100644 index 00000000..d86c4a44 --- /dev/null +++ b/ext/libpng/projects/visualc71/libpng_VS2003.vcproj @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/libpng/projects/visualc71/zlib.vcproj b/ext/libpng/projects/visualc71/zlib.vcproj new file mode 100644 index 00000000..cf50d1af --- /dev/null +++ b/ext/libpng/projects/visualc71/zlib.vcproj @@ -0,0 +1,452 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/libpng/projects/visualc71/zlib_2005Express.vcproj b/ext/libpng/projects/visualc71/zlib_2005Express.vcproj new file mode 100644 index 00000000..b93f567a --- /dev/null +++ b/ext/libpng/projects/visualc71/zlib_2005Express.vcproj @@ -0,0 +1,451 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/libpng/projects/visualc71/zlib_VS2003.vcproj b/ext/libpng/projects/visualc71/zlib_VS2003.vcproj new file mode 100644 index 00000000..527abe8c --- /dev/null +++ b/ext/libpng/projects/visualc71/zlib_VS2003.vcproj @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/libpng/scripts/pngw32.aps b/ext/libpng/scripts/pngw32.aps new file mode 100644 index 00000000..354cdf69 Binary files /dev/null and b/ext/libpng/scripts/pngw32.aps differ diff --git a/ext/libpng/scripts/pngw32.rc b/ext/libpng/scripts/pngw32.rc new file mode 100644 index 00000000..f6cb0f0e --- /dev/null +++ b/ext/libpng/scripts/pngw32.rc @@ -0,0 +1,112 @@ +#define PNG_VERSION_INFO_ONLY + +#include +#include "../png.h" + +#define _QUOTE(x) # x +#define QUOTE(x) _QUOTE(x) + +#define PNG_LIBPNG_DLLFNAME "LIBPNG" + +/* Support deprecated PRIVATEBUILD macro */ +#if defined(PRIVATEBUILD) && !defined(PNG_USER_PRIVATEBUILD) +# define PNG_USER_PRIVATEBUILD PRIVATEBUILD +#endif + +#if defined(PNG_USER_DLLFNAME_POSTFIX) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined as a string describing the\ + custom changes made to the library." +#endif + +/* Prioritize PNG_USER_x over PNG_LIBPNG_x */ +#ifdef PNG_USER_DLLFNAME_POSTFIX +# undef PNG_LIBPNG_DLLFNAME_POSTFIX +# define PNG_LIBPNG_DLLFNAME_POSTFIX PNG_USER_DLLFNAME_POSTFIX +#endif + +#ifdef PNG_USER_VERSIONINFO_COMMENTS +# undef PNG_LIBPNG_VERSIONINFO_COMMENTS +# define PNG_LIBPNG_VERSIONINFO_COMMENTS PNG_USER_VERSIONINFO_COMMENTS +#endif + +#if defined(PNG_DEBUG) && (PNG_DEBUG > 0) +# define VS_DEBUG VS_FF_DEBUG +# ifndef PNG_LIBPNG_DLLFNAME_POSTFIX +# define PNG_LIBPNG_DLLFNAME_POSTFIX "D" +# endif /* PNG_LIBPNG_DLLFNAME_POSTFIX */ +# ifndef PNG_LIBPNG_VERSIONINFO_COMMENTS +# define PNG_LIBPNG_VERSIONINFO_COMMENTS "PNG_DEBUG=" QUOTE(PNG_DEBUG) +# endif /* PNG_LIBPNG_VERSIONINFO_COMMENTS */ +#else +# define VS_DEBUG 0 +# ifndef PNG_LIBPNG_DLLFNAME_POSTFIX +# define PNG_LIBPNG_DLLFNAME_POSTFIX +# endif /* PNG_LIBPNG_DLLFNAME_POSTFIX */ +#endif /* defined(DEBUG)... */ + +#ifdef PNG_USER_PRIVATEBUILD +# define VS_PRIVATEBUILD VS_FF_PRIVATEBUILD +#else +# define VS_PRIVATEBUILD 0 +#endif /* PNG_USER_PRIVATEBUILD */ + +#ifdef PNG_LIBPNG_SPECIALBUILD +# define VS_SPECIALBUILD VS_FF_SPECIALBUILD +#else +# define VS_SPECIALBUILD 0 +#endif /* PNG_LIBPNG_BUILD_SPECIAL */ + +#if ((PNG_LIBPNG_BUILD_BASE_TYPE & PNG_LIBPNG_RELEASE_STATUS_MASK) !=\ + PNG_LIBPNG_BUILD_STABLE) +# define VS_PRERELEASE VS_FF_PRERELEASE +# define VS_PATCHED 0 +#else +# define VS_PRERELEASE 0 +# if (PNG_LIBPNG_BUILD_BASE_TYPE & PNG_LIBPNG_BUILD_PATCHED) +# define VS_PATCHED VS_FF_PATCHED +# else +# define VS_PATCHED 0 +# endif +#endif + +VS_VERSION_INFO VERSIONINFO +FILEVERSION PNG_LIBPNG_VER_MAJOR, PNG_LIBPNG_VER_MINOR, PNG_LIBPNG_VER_RELEASE, PNG_LIBPNG_VER_BUILD +PRODUCTVERSION PNG_LIBPNG_VER_MAJOR, PNG_LIBPNG_VER_MINOR, PNG_LIBPNG_VER_RELEASE, PNG_LIBPNG_VER_BUILD +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS VS_DEBUG | VS_PRIVATEBUILD | VS_SPECIALBUILD | VS_PRERELEASE | VS_PATCHED +FILEOS VOS__WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN BLOCK "040904E4" /* Language type = U.S English(0x0409) and Character Set = Windows, Multilingual(0x04E4) */ + BEGIN +#ifdef PNG_LIBPNG_VERSIONINFO_COMMENTS + VALUE "Comments", PNG_LIBPNG_VERSIONINFO_COMMENTS "\000" +#endif /* PNG_LIBPNG_VERSIONINFO_COMMENTS */ +#ifdef PNG_USER_VERSIONINFO_COMPANYNAME + VALUE "CompanyName", PNG_USER_VERSIONINFO_COMPANYNAME "\000" +#endif /* PNG_USER_VERSIONINFO_COMPANYNAME */ + VALUE "FileDescription", "PNG image compression library\000" + VALUE "FileVersion", PNG_LIBPNG_VER_STRING "\000" + VALUE "InternalName", PNG_LIBPNG_DLLFNAME QUOTE(PNG_LIBPNG_VER_DLLNUM) PNG_LIBPNG_DLLFNAME_POSTFIX " (Windows 32 bit)\000" + VALUE "LegalCopyright", "\251 1998-2004 Glenn Randers-Pehrson et al.\000" +#ifdef PNG_USER_VERSIONINFO_LEGALTRADEMARKS + VALUE "LegalTrademarks", PNG_USER_VERSIONINFO_LEGALTRADEMARKS "\000" +#endif /* PNG_USER_VERSIONINFO_LEGALTRADEMARKS */ + VALUE "OriginalFilename", PNG_LIBPNG_DLLFNAME QUOTE(PNG_LIBPNG_VER_DLLNUM) PNG_LIBPNG_DLLFNAME_POSTFIX ".DLL\000" +#ifdef PNG_USER_PRIVATEBUILD + VALUE "PrivateBuild", PNG_USER_PRIVATEBUILD "\000" +#endif /* PNG_USER_PRIVATEBUILD */ + VALUE "ProductName", "LibPNG\000" + VALUE "ProductVersion", "1\000" +#ifdef PNG_LIBPNG_SPECIALBUILD + VALUE "SpecialBuild", PNG_LIBPNG_SPECIALBUILD "\000" +#endif /* PNG_LIBPNG_SPECIALBUILD */ + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04E4 + END +END diff --git a/ext/utf8/doc/ReleaseNotes b/ext/utf8/doc/ReleaseNotes new file mode 100644 index 00000000..8541c7a6 --- /dev/null +++ b/ext/utf8/doc/ReleaseNotes @@ -0,0 +1,9 @@ +utf8 cpp library +Release 2.1 + +This is a minor feature release - added the function peek_next. + +Changes from version 2.o +- Implemented feature request [ 1770746 ] "Provide a const version of next() (some sort of a peek() ) + +Files included in the release: utf8.h, core.h, checked.h, unchecked.h, utf8cpp.html, ReleaseNotes diff --git a/ext/utf8/doc/utf8cpp.html b/ext/utf8/doc/utf8cpp.html new file mode 100644 index 00000000..4ad7e100 --- /dev/null +++ b/ext/utf8/doc/utf8cpp.html @@ -0,0 +1,1574 @@ + + + + + + + + + UTF8-CPP: UTF-8 with C++ in a Portable Way + + + + +

+ UTF8-CPP: UTF-8 with C++ in a Portable Way +

+

+ The Sourceforge project page +

+ +

+ Introduction +

+

+ Many C++ developers miss an easy and portable way of handling Unicode encoded + strings. C++ Standard is currently Unicode agnostic, and while some work is being + done to introduce Unicode to the next incarnation called C++0x, for the moment + nothing of the sort is available. In the meantime, developers use 3rd party + libraries like ICU, OS specific capabilities, or simply roll out their own + solutions. +

+

+ In order to easily handle UTF-8 encoded Unicode strings, I have come up with a small + generic library. For anybody used to work with STL algorithms and iterators, it should be + easy and natural to use. The code is freely available for any purpose - check out + the license at the beginning of the utf8.h file. If you run into + bugs or performance issues, please let me know and I'll do my best to address them. +

+

+ The purpose of this article is not to offer an introduction to Unicode in general, + and UTF-8 in particular. If you are not familiar with Unicode, be sure to check out + Unicode Home Page or some other source of + information for Unicode. Also, it is not my aim to advocate the use of UTF-8 + encoded strings in C++ programs; if you want to handle UTF-8 encoded strings from + C++, I am sure you have good reasons for it. +

+

+ Examples of use +

+

+ To illustrate the use of this utf8 library, we shall open a file containing UTF-8 + encoded text, check whether it starts with a byte order mark, read each line into a + std::string, check it for validity, convert the text to UTF-16, and + back to UTF-8: +

+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+#include "utf8.h"
+using namespace std;
+int main()
+{
+    if (argc != 2) {
+        cout << "\nUsage: docsample filename\n";
+        return 0;
+    }
+    const char* test_file_path = argv[1];
+    // Open the test file (must be UTF-8 encoded)
+    ifstream fs8(test_file_path);
+    if (!fs8.is_open()) {
+    cout << "Could not open " << test_file_path << endl;
+    return 0;
+    }
+    // Read the first line of the file
+    unsigned line_count = 1;
+    string line;
+    if (!getline(fs8, line)) 
+        return 0;
+    // Look for utf-8 byte-order mark at the beginning
+    if (line.size() > 2) {
+        if (utf8::is_bom(line.c_str()))
+            cout << "There is a byte order mark at the beginning of the file\n";
+    }
+    // Play with all the lines in the file
+    do {
+       // check for invalid utf-8 (for a simple yes/no check, there is also utf8::is_valid function)
+        string::iterator end_it = utf8::find_invalid(line.begin(), line.end());
+        if (end_it != line.end()) {
+            cout << "Invalid UTF-8 encoding detected at line " << line_count << "\n";
+            cout << "This part is fine: " << string(line.begin(), end_it) << "\n";
+        }
+        // Get the line length (at least for the valid part)
+        int length = utf8::distance(line.begin(), end_it);
+        cout << "Length of line " << line_count << " is " << length <<  "\n";
+        // Convert it to utf-16
+        vector<unsigned short> utf16line;
+        utf8::utf8to16(line.begin(), end_it, back_inserter(utf16line));
+        // And back to utf-8
+        string utf8line; 
+        utf8::utf16to8(utf16line.begin(), utf16line.end(), back_inserter(utf8line));
+        // Confirm that the conversion went OK:
+        if (utf8line != string(line.begin(), end_it))
+            cout << "Error in UTF-16 conversion at line: " << line_count << "\n";        
+        getline(fs8, line);
+        line_count++;
+    } while (!fs8.eof());
+    return 0;
+}
+
+

+ In the previous code sample, we have seen the use of the following functions from + utf8 namespace: first we used is_bom function to detect + UTF-8 byte order mark at the beginning of the file; then for each line we performed + a detection of invalid UTF-8 sequences with find_invalid; the number + of characters (more precisely - the number of Unicode code points) in each line was + determined with a use of utf8::distance; finally, we have converted + each line to UTF-16 encoding with utf8to16 and back to UTF-8 with + utf16to8. +

+

+ Reference +

+

+ Functions From utf8 Namespace +

+

+ utf8::append +

+

+ Available in version 1.0 and later. +

+

+ Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence + to a UTF-8 string. +

+
+template <typename octet_iterator>
+octet_iterator append(uint32_t cp, octet_iterator result);
+   
+
+

+ cp: A 32 bit integer representing a code point to append to the + sequence.
+ result: An output iterator to the place in the sequence where to + append the code point.
+ Return value: An iterator pointing to the place + after the newly appended sequence. +

+

+ Example of use: +

+
+unsigned char u[5] = {0,0,0,0,0};
+unsigned char* end = append(0x0448, u);
+assert (u[0] == 0xd1 && u[1] == 0x88 && u[2] == 0 && u[3] == 0 && u[4] == 0);
+
+

+ Note that append does not allocate any memory - it is the burden of + the caller to make sure there is enough memory allocated for the operation. To make + things more interesting, append can add anywhere between 1 and 4 + octets to the sequence. In practice, you would most often want to use + std::back_inserter to ensure that the necessary memory is allocated. +

+

+ In case of an invalid code point, a utf8::invalid_code_point exception + is thrown. +

+

+ utf8::next +

+

+ Available in version 1.0 and later. +

+

+ Given the iterator to the beginning of the UTF-8 sequence, it returns the code + point and moves the iterator to the next position. +

+
+template <typename octet_iterator> 
+uint32_t next(octet_iterator& it, octet_iterator end);
+   
+
+

+ it: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + beginning of the next code point.
+ end: end of the UTF-8 sequence to be processed. If it + gets equal to end during the extraction of a code point, an + utf8::not_enough_room exception is thrown.
+ Return value: the 32 bit representation of the + processed UTF-8 code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+int cp = next(w, twochars + 6);
+assert (cp == 0x65e5);
+assert (w == twochars + 3);
+
+

+ This function is typically used to iterate through a UTF-8 encoded string. +

+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. +

+

+ utf8::peek_next +

+

+ Available in version 2.1 and later. +

+

+ Given the iterator to the beginning of the UTF-8 sequence, it returns the code + point for the following sequence without changing the value of the iterator. +

+
+template <typename octet_iterator> 
+uint32_t peek_next(octet_iterator it, octet_iterator end);
+   
+
+

+ it: an iterator pointing to the beginning of an UTF-8 + encoded code point.
+ end: end of the UTF-8 sequence to be processed. If it + gets equal to end during the extraction of a code point, an + utf8::not_enough_room exception is thrown.
+ Return value: the 32 bit representation of the + processed UTF-8 code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+int cp = peek_next(w, twochars + 6);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. +

+

+ utf8::prior +

+

+ Available in version 1.02 and later. +

+

+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. +

+
+template <typename octet_iterator> 
+uint32_t prior(octet_iterator& it, octet_iterator start);
+   
+
+

+ it: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.
+ start: an iterator to the beginning of the sequence where the search + for the beginning of a code point is performed. It is a + safety measure to prevent passing the beginning of the string in the search for a + UTF-8 lead octet.
+ Return value: the 32 bit representation of the + previous code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+unsigned char* w = twochars + 3;
+int cp = prior (w, twochars);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ This function has two purposes: one is two iterate backwards through a UTF-8 + encoded string. Note that it is usually a better idea to iterate forward instead, + since utf8::next is faster. The second purpose is to find a beginning + of a UTF-8 sequence if we have a random position within a string. +

+

+ it will typically point to the beginning of + a code point, and start will point to the + beginning of the string to ensure we don't go backwards too far. it is + decreased until it points to a lead UTF-8 octet, and then the UTF-8 sequence + beginning with that octet is decoded to a 32 bit representation and returned. +

+

+ In case pass_end is reached before a UTF-8 lead octet is hit, or if an + invalid UTF-8 sequence is started by the lead octet, an invalid_utf8 + exception is thrown. +

+

+ utf8::previous +

+

+ Deprecated in version 1.02 and later. +

+

+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. +

+
+template <typename octet_iterator> 
+uint32_t previous(octet_iterator& it, octet_iterator pass_start);
+   
+
+

+ it: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.
+ pass_start: an iterator to the point in the sequence where the search + for the beginning of a code point is aborted if no result was reached. It is a + safety measure to prevent passing the beginning of the string in the search for a + UTF-8 lead octet.
+ Return value: the 32 bit representation of the + previous code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+unsigned char* w = twochars + 3;
+int cp = previous (w, twochars - 1);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ utf8::previous is deprecated, and utf8::prior should + be used instead, although the existing code can continue using this function. + The problem is the parameter pass_start that points to the position + just before the beginning of the sequence. Standard containers don't have the + concept of "pass start" and the function can not be used with their iterators. +

+

+ it will typically point to the beginning of + a code point, and pass_start will point to the octet just before the + beginning of the string to ensure we don't go backwards too far. it is + decreased until it points to a lead UTF-8 octet, and then the UTF-8 sequence + beginning with that octet is decoded to a 32 bit representation and returned. +

+

+ In case pass_end is reached before a UTF-8 lead octet is hit, or if an + invalid UTF-8 sequence is started by the lead octet, an invalid_utf8 + exception is thrown +

+

+ utf8::advance +

+

+ Available in version 1.0 and later. +

+

+ Advances an iterator by the specified number of code points within an UTF-8 + sequence. +

+
+template <typename octet_iterator, typename distance_type> 
+void advance (octet_iterator& it, distance_type n, octet_iterator end);
+   
+
+

+ it: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + nth following code point.
+ n: a positive integer that shows how many code points we want to + advance.
+ end: end of the UTF-8 sequence to be processed. If it + gets equal to end during the extraction of a code point, an + utf8::not_enough_room exception is thrown.
+

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+unsigned char* w = twochars;
+advance (w, 2, twochars + 6);
+assert (w == twochars + 5);
+
+

+ This function works only "forward". In case of a negative n, there is + no effect. +

+

+ In case of an invalid code point, a utf8::invalid_code_point exception + is thrown. +

+

+ utf8::distance +

+

+ Available in version 1.0 and later. +

+

+ Given the iterators to two UTF-8 encoded code points in a seqence, returns the + number of code points between them. +

+
+template <typename octet_iterator> 
+typename std::iterator_traits<octet_iterator>::difference_type distance (octet_iterator first, octet_iterator last);
+   
+
+

+ first: an iterator to a beginning of a UTF-8 encoded code point.
+ last: an iterator to a "post-end" of the last UTF-8 encoded code + point in the sequence we are trying to determine the length. It can be the + beginning of a new code point, or not.
+ Return value the distance between the iterators, + in code points. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+size_t dist = utf8::distance(twochars, twochars + 5);
+assert (dist == 2);
+
+

+ This function is used to find the length (in code points) of a UTF-8 encoded + string. The reason it is called distance, rather than, say, + length is mainly because developers are used that length is an + O(1) function. Computing the length of an UTF-8 string is a linear operation, and + it looked better to model it after std::distance algorithm. +

+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. If last does not point to the past-of-end of a UTF-8 seqence, + a utf8::not_enough_room exception is thrown. +

+

+ utf8::utf16to8 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-16 encoded string to UTF-8. +

+
+template <typename u16bit_iterator, typename octet_iterator>
+octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-16 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-16 encoded + string to convert.
+ result: an output iterator to the place in the UTF-8 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-8 string. +

+

+ Example of use: +

+
+unsigned short utf16string[] = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e};
+vector<unsigned char> utf8result;
+utf16to8(utf16string, utf16string + 5, back_inserter(utf8result));
+assert (utf8result.size() == 10);    
+
+

+ In case of invalid UTF-16 sequence, a utf8::invalid_utf16 exception is + thrown. +

+

+ utf8::utf8to16 +

+

+ Available in version 1.0 and later. +

+

+ Converts an UTF-8 encoded string to UTF-16 +

+
+template <typename u16bit_iterator, typename octet_iterator>
+u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-8 encoded + string to convert. < br /> end: an iterator pointing to + pass-the-end of the UTF-8 encoded string to convert.
+ result: an output iterator to the place in the UTF-16 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-16 string. +

+

+ Example of use: +

+
+char utf8_with_surrogates[] = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e";
+vector <unsigned short> utf16result;
+utf8to16(utf8_with_surrogates, utf8_with_surrogates + 9, back_inserter(utf16result));
+assert (utf16result.size() == 4);
+assert (utf16result[2] == 0xd834);
+assert (utf16result[3] == 0xdd1e);
+
+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. If end does not point to the past-of-end of a UTF-8 seqence, a + utf8::not_enough_room exception is thrown. +

+

+ utf8::utf32to8 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-32 encoded string to UTF-8. +

+
+template <typename octet_iterator, typename u32bit_iterator>
+octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-32 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-32 encoded + string to convert.
+ result: an output iterator to the place in the UTF-8 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-8 string. +

+

+ Example of use: +

+
+int utf32string[] = {0x448, 0x65E5, 0x10346, 0};
+vector<unsigned char> utf8result;
+utf32to8(utf32string, utf32string + 3, back_inserter(utf8result));
+assert (utf8result.size() == 9);
+
+

+ In case of invalid UTF-32 string, a utf8::invalid_code_point exception + is thrown. +

+

+ utf8::utf8to32 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-8 encoded string to UTF-32. +

+
+template <typename octet_iterator, typename u32bit_iterator>
+u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-8 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-8 encoded string + to convert.
+ result: an output iterator to the place in the UTF-32 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-32 string. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+vector<int> utf32result;
+utf8to32(twochars, twochars + 5, back_inserter(utf32result));
+assert (utf32result.size() == 2);
+
+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. If end does not point to the past-of-end of a UTF-8 seqence, a + utf8::not_enough_room exception is thrown. +

+

+ utf8::find_invalid +

+

+ Available in version 1.0 and later. +

+

+ Detects an invalid sequence within a UTF-8 string. +

+
+template <typename octet_iterator> 
+octet_iterator find_invalid(octet_iterator start, octet_iterator end);
+
+

+ start: an iterator pointing to the beginning of the UTF-8 string to + test for validity.
+ end: an iterator pointing to pass-the-end of the UTF-8 string to test + for validity.
+ Return value: an iterator pointing to the first + invalid octet in the UTF-8 string. In case none were found, equals + end. +

+

+ Example of use: +

+
+char utf_invalid[] = "\xe6\x97\xa5\xd1\x88\xfa";
+char* invalid = find_invalid(utf_invalid, utf_invalid + 6);
+assert (invalid == utf_invalid + 5);
+
+

+ This function is typically used to make sure a UTF-8 string is valid before + processing it with other functions. It is especially important to call it if before + doing any of the unchecked operations on it. +

+

+ utf8::is_valid +

+

+ Available in version 1.0 and later. +

+

+ Checks whether a sequence of octets is a valid UTF-8 string. +

+
+template <typename octet_iterator> 
+bool is_valid(octet_iterator start, octet_iterator end);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-8 string to + test for validity.
+ end: an iterator pointing to pass-the-end of the UTF-8 string to test + for validity.
+ Return value: true if the sequence + is a valid UTF-8 string; false if not. +

+ Example of use: +
+char utf_invalid[] = "\xe6\x97\xa5\xd1\x88\xfa";
+bool bvalid = is_valid(utf_invalid, utf_invalid + 6);
+assert (bvalid == false);
+
+

+ is_valid is a shorthand for find_invalid(start, end) == + end;. You may want to use it to make sure that a byte seqence is a valid + UTF-8 string without the need to know where it fails if it is not valid. +

+

+ utf8::replace_invalid +

+

+ Available in version 2.0 and later. +

+

+ Replaces all invalid UTF-8 sequences within a string with a replacement marker. +

+
+template <typename octet_iterator, typename output_iterator>
+output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement);
+template <typename octet_iterator, typename output_iterator>
+output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-8 string to + look for invalid UTF-8 sequences.
+ end: an iterator pointing to pass-the-end of the UTF-8 string to look + for invalid UTF-8 sequences.
+ out: An output iterator to the range where the result of replacement + is stored.
+ replacement: A Unicode code point for the replacement marker. The + version without this parameter assumes the value 0xfffd
+ Return value: An iterator pointing to the place + after the UTF-8 string with replaced invalid sequences. +

+

+ Example of use: +

+
+char invalid_sequence[] = "a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z";
+vector<char> replace_invalid_result;
+replace_invalid (invalid_sequence, invalid_sequence + sizeof(invalid_sequence), back_inserter(replace_invalid_result), '?');
+bvalid = is_valid(replace_invalid_result.begin(), replace_invalid_result.end());
+assert (bvalid);
+char* fixed_invalid_sequence = "a????z";
+assert (std::equal(replace_invalid_result.begin(), replace_invalid_result.end(), fixed_invalid_sequence));
+
+

+ replace_invalid does not perform in-place replacement of invalid + sequences. Rather, it produces a copy of the original string with the invalid + sequences replaced with a replacement marker. Therefore, out must not + be in the [start, end] range. +

+

+ If end does not point to the past-of-end of a UTF-8 sequence, a + utf8::not_enough_room exception is thrown. +

+

+ utf8::is_bom +

+

+ Available in version 1.0 and later. +

+

+ Checks whether a sequence of three octets is a UTF-8 byte order mark (BOM) +

+
+template <typename octet_iterator> 
+bool is_bom (octet_iterator it);
+
+

+ it: beginning of the 3-octet sequence to check
+ Return value: true if the sequence + is UTF-8 byte order mark; false if not. +

+

+ Example of use: +

+
+unsigned char byte_order_mark[] = {0xef, 0xbb, 0xbf};
+bool bbom = is_bom(byte_order_mark);
+assert (bbom == true);
+
+

+ The typical use of this function is to check the first three bytes of a file. If + they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8 + encoded text. +

+

+ Types From utf8 Namespace +

+

+ utf8::iterator +

+

+ Available in version 2.0 and later. +

+

+ Adapts the underlying octet iterator to iterate over the sequence of code points, + rather than raw octets. +

+
+template <typename octet_iterator>
+class iterator;
+
+ +
Member functions
+
+
iterator();
the deafult constructor; the underlying octet_iterator is + constructed with its default constructor. +
explicit iterator (const octet_iterator& octet_it, + const octet_iterator& range_start, + const octet_iterator& range_end);
a constructor + that initializes the underlying octet_iterator with octet_it + and sets the range in which the iterator is considered valid. +
octet_iterator base () const;
returns the + underlying octet_iterator. +
uint32_t operator * () const;
decodes the utf-8 sequence + the underlying octet_iterator is pointing to and returns the code point. +
bool operator == (const iterator& rhs) + const;
returns true + if the two underlaying iterators are equal. +
bool operator != (const iterator& rhs) + const;
returns true + if the two underlaying iterators are not equal. +
iterator& operator ++ ();
the prefix increment - moves + the iterator to the next UTF-8 encoded code point. +
iterator operator ++ (int);
+ the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one. +
iterator& operator -- ();
the prefix decrement - moves + the iterator to the previous UTF-8 encoded code point. +
iterator operator -- (int);
+ the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one. +
+

+ Example of use: +

+
+char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88";
+utf8::iterator<char*> it(threechars, threechars, threechars + 9);
+utf8::iterator<char*> it2 = it;
+assert (it2 == it);
+assert (*it == 0x10346);
+assert (*(++it) == 0x65e5);
+assert ((*it++) == 0x65e5);
+assert (*it == 0x0448);
+assert (it != it2);
+utf8::iterator<char*> endit (threechars + 9, threechars, threechars + 9);  
+assert (++it == endit);
+assert (*(--it) == 0x0448);
+assert ((*it--) == 0x0448);
+assert (*it == 0x65e5);
+assert (--it == utf8::iterator<char*>(threechars, threechars, threechars + 9));
+assert (*it == 0x10346);
+
+

+ The purpose of utf8::iterator adapter is to enable easy iteration as well as the use of STL + algorithms with UTF-8 encoded strings. Increment and decrement operators are implemented in terms of + utf8::next() and utf8::prior() functions. +

+

+ Note that utf8::iterator adapter is a checked iterator. It operates on the range specified in + the constructor; any attempt to go out of that range will result in an exception. Even the comparison operators + require both iterator object to be constructed against the same range - otherwise an exception is thrown. Typically, + the range will be determined by sequence container functions begin and end, i.e.: +

+
+std::string s = "example";
+utf8::iterator i (s.begin(), s.begin(), s.end());
+
+

+ Functions From utf8::unchecked Namespace +

+

+ utf8::unchecked::append +

+

+ Available in version 1.0 and later. +

+

+ Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence + to a UTF-8 string. +

+
+template <typename octet_iterator>
+octet_iterator append(uint32_t cp, octet_iterator result);
+   
+
+

+ cp: A 32 bit integer representing a code point to append to the + sequence.
+ result: An output iterator to the place in the sequence where to + append the code point.
+ Return value: An iterator pointing to the place + after the newly appended sequence. +

+

+ Example of use: +

+
+unsigned char u[5] = {0,0,0,0,0};
+unsigned char* end = unchecked::append(0x0448, u);
+assert (u[0] == 0xd1 && u[1] == 0x88 && u[2] == 0 && u[3] == 0 && u[4] == 0);
+
+

+ This is a faster but less safe version of utf8::append. It does not + check for validity of the supplied code point, and may produce an invalid UTF-8 + sequence. +

+

+ utf8::unchecked::next +

+

+ Available in version 1.0 and later. +

+

+ Given the iterator to the beginning of a UTF-8 sequence, it returns the code point + and moves the iterator to the next position. +

+
+template <typename octet_iterator>
+uint32_t next(octet_iterator& it);
+   
+
+

+ it: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + beginning of the next code point.
+ Return value: the 32 bit representation of the + processed UTF-8 code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+int cp = unchecked::next(w);
+assert (cp == 0x65e5);
+assert (w == twochars + 3);
+
+

+ This is a faster but less safe version of utf8::next. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ utf8::unchecked::peek_next +

+

+ Available in version 2.1 and later. +

+

+ Given the iterator to the beginning of a UTF-8 sequence, it returns the code point. +

+
+template <typename octet_iterator>
+uint32_t peek_next(octet_iterator it);
+   
+
+

+ it: an iterator pointing to the beginning of an UTF-8 + encoded code point.
+ Return value: the 32 bit representation of the + processed UTF-8 code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+int cp = unchecked::peek_next(w);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ This is a faster but less safe version of utf8::peek_next. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ utf8::unchecked::prior +

+

+ Available in version 1.02 and later. +

+

+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. +

+
+template <typename octet_iterator>
+uint32_t prior(octet_iterator& it);
+   
+
+

+ it: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.
+ Return value: the 32 bit representation of the + previous code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars + 3;
+int cp = unchecked::prior (w);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ This is a faster but less safe version of utf8::prior. It does not + check for validity of the supplied UTF-8 sequence and offers no boundary checking. +

+

+ utf8::unchecked::previous (deprecated, see utf8::unchecked::prior) +

+

+ Deprecated in version 1.02 and later. +

+

+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. +

+
+template <typename octet_iterator>
+uint32_t previous(octet_iterator& it);
+   
+
+

+ it: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.
+ Return value: the 32 bit representation of the + previous code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars + 3;
+int cp = unchecked::previous (w);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ The reason this function is deprecated is just the consistency with the "checked" + versions, where prior should be used instead of previous. + In fact, unchecked::previous behaves exactly the same as + unchecked::prior +

+

+ This is a faster but less safe version of utf8::previous. It does not + check for validity of the supplied UTF-8 sequence and offers no boundary checking. +

+

+ utf8::unchecked::advance +

+

+ Available in version 1.0 and later. +

+

+ Advances an iterator by the specified number of code points within an UTF-8 + sequence. +

+
+template <typename octet_iterator, typename distance_type>
+void advance (octet_iterator& it, distance_type n);
+   
+
+

+ it: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + nth following code point.
+ n: a positive integer that shows how many code points we want to + advance.
+

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+unchecked::advance (w, 2);
+assert (w == twochars + 5);
+
+

+ This function works only "forward". In case of a negative n, there is + no effect. +

+

+ This is a faster but less safe version of utf8::advance. It does not + check for validity of the supplied UTF-8 sequence and offers no boundary checking. +

+

+ utf8::unchecked::distance +

+

+ Available in version 1.0 and later. +

+

+ Given the iterators to two UTF-8 encoded code points in a seqence, returns the + number of code points between them. +

+
+template <typename octet_iterator>
+typename std::iterator_traits<octet_iterator>::difference_type distance (octet_iterator first, octet_iterator last);
+
+

+ first: an iterator to a beginning of a UTF-8 encoded code point.
+ last: an iterator to a "post-end" of the last UTF-8 encoded code + point in the sequence we are trying to determine the length. It can be the + beginning of a new code point, or not.
+ Return value the distance between the iterators, + in code points. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+size_t dist = utf8::unchecked::distance(twochars, twochars + 5);
+assert (dist == 2);
+
+

+ This is a faster but less safe version of utf8::distance. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ utf8::unchecked::utf16to8 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-16 encoded string to UTF-8. +

+
+template <typename u16bit_iterator, typename octet_iterator>
+octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-16 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-16 encoded + string to convert.
+ result: an output iterator to the place in the UTF-8 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-8 string. +

+

+ Example of use: +

+
+unsigned short utf16string[] = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e};
+vector<unsigned char> utf8result;
+unchecked::utf16to8(utf16string, utf16string + 5, back_inserter(utf8result));
+assert (utf8result.size() == 10);    
+
+

+ This is a faster but less safe version of utf8::utf16to8. It does not + check for validity of the supplied UTF-16 sequence. +

+

+ utf8::unchecked::utf8to16 +

+

+ Available in version 1.0 and later. +

+

+ Converts an UTF-8 encoded string to UTF-16 +

+
+template <typename u16bit_iterator, typename octet_iterator>
+u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-8 encoded + string to convert. < br /> end: an iterator pointing to + pass-the-end of the UTF-8 encoded string to convert.
+ result: an output iterator to the place in the UTF-16 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-16 string. +

+

+ Example of use: +

+
+char utf8_with_surrogates[] = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e";
+vector <unsigned short> utf16result;
+unchecked::utf8to16(utf8_with_surrogates, utf8_with_surrogates + 9, back_inserter(utf16result));
+assert (utf16result.size() == 4);
+assert (utf16result[2] == 0xd834);
+assert (utf16result[3] == 0xdd1e);
+
+

+ This is a faster but less safe version of utf8::utf8to16. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ utf8::unchecked::utf32to8 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-32 encoded string to UTF-8. +

+
+template <typename octet_iterator, typename u32bit_iterator>
+octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-32 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-32 encoded + string to convert.
+ result: an output iterator to the place in the UTF-8 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-8 string. +

+

+ Example of use: +

+
+int utf32string[] = {0x448, 0x65e5, 0x10346, 0};
+vector<unsigned char> utf8result;
+utf32to8(utf32string, utf32string + 3, back_inserter(utf8result));
+assert (utf8result.size() == 9);
+
+

+ This is a faster but less safe version of utf8::utf32to8. It does not + check for validity of the supplied UTF-32 sequence. +

+

+ utf8::unchecked::utf8to32 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-8 encoded string to UTF-32. +

+
+template <typename octet_iterator, typename u32bit_iterator>
+u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-8 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-8 encoded string + to convert.
+ result: an output iterator to the place in the UTF-32 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-32 string. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+vector<int> utf32result;
+unchecked::utf8to32(twochars, twochars + 5, back_inserter(utf32result));
+assert (utf32result.size() == 2);
+
+

+ This is a faster but less safe version of utf8::utf8to32. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ Types From utf8::unchecked Namespace +

+

+ utf8::iterator +

+

+ Available in version 2.0 and later. +

+

+ Adapts the underlying octet iterator to iterate over the sequence of code points, + rather than raw octets. +

+
+template <typename octet_iterator>
+class iterator;
+
+ +
Member functions
+
+
iterator();
the deafult constructor; the underlying octet_iterator is + constructed with its default constructor. +
explicit iterator (const octet_iterator& octet_it); +
a constructor + that initializes the underlying octet_iterator with octet_it +
octet_iterator base () const;
returns the + underlying octet_iterator. +
uint32_t operator * () const;
decodes the utf-8 sequence + the underlying octet_iterator is pointing to and returns the code point. +
bool operator == (const iterator& rhs) + const;
returns true + if the two underlaying iterators are equal. +
bool operator != (const iterator& rhs) + const;
returns true + if the two underlaying iterators are not equal. +
iterator& operator ++ ();
the prefix increment - moves + the iterator to the next UTF-8 encoded code point. +
iterator operator ++ (int);
+ the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one. +
iterator& operator -- ();
the prefix decrement - moves + the iterator to the previous UTF-8 encoded code point. +
iterator operator -- (int);
+ the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one. +
+

+ Example of use: +

+
+char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88";
+utf8::unchecked::iterator<char*> un_it(threechars);
+utf8::unchecked::iterator<char*> un_it2 = un_it;
+assert (un_it2 == un_it);
+assert (*un_it == 0x10346);
+assert (*(++un_it) == 0x65e5);
+assert ((*un_it++) == 0x65e5);
+assert (*un_it == 0x0448);
+assert (un_it != un_it2);
+utf8::::unchecked::iterator<char*> un_endit (threechars + 9);  
+assert (++un_it == un_endit);
+assert (*(--un_it) == 0x0448);
+assert ((*un_it--) == 0x0448);
+assert (*un_it == 0x65e5);
+assert (--un_it == utf8::unchecked::iterator<char*>(threechars));
+assert (*un_it == 0x10346);
+
+

+ This is an unchecked version of utf8::iterator. It is faster in many cases, but offers + no validity or range checks. +

+

+ Points of interest +

+

+ Design goals and decisions +

+

+ The library was designed to be: +

+
    +
  1. + Generic: for better or worse, there are many C++ string classes out there, and + the library should work with as many of them as possible. +
  2. +
  3. + Portable: the library should be portable both accross different platforms and + compilers. The only non-portable code is a small section that declares unsigned + integers of different sizes: three typedefs. They can be changed by the users of + the library if they don't match their platform. The default setting should work + for Windows (both 32 and 64 bit), and most 32 bit and 64 bit Unix derivatives. +
  4. +
  5. + Lightweight: follow the "pay only for what you use" guidline. +
  6. +
  7. + Unintrusive: avoid forcing any particular design or even programming style on the + user. This is a library, not a framework. +
  8. +
+

+ Alternatives +

+

+ In case you want to look into other means of working with UTF-8 strings from C++, + here is the list of solutions I am aware of: +

+
    +
  1. + ICU Library. It is very powerful, + complete, feature-rich, mature, and widely used. Also big, intrusive, + non-generic, and doesn't play well with the Standard Library. I definitelly + recommend looking at ICU even if you don't plan to use it. +
  2. +
  3. + Glib::ustring. + A class specifically made to work with UTF-8 strings, and also feel like + std::string. If you prefer to have yet another string class in your + code, it may be worth a look. Be aware of the licensing issues, though. +
  4. +
  5. + Platform dependent solutions: Windows and POSIX have functions to convert strings + from one encoding to another. That is only a subset of what my library offers, + but if that is all you need it may be good enough, especially given the fact that + these functions are mature and tested in production. +
  6. +
+

+ Conclusion +

+

+ Until Unicode becomes officially recognized by the C++ Standard Library, we need to + use other means to work with UTF-8 strings. Template functions I describe in this + article may be a good step in this direction. +

+ +
    +
  1. + The Unicode Consortium. +
  2. +
  3. + ICU Library. +
  4. +
  5. + UTF-8 at Wikipedia +
  6. +
  7. + UTF-8 and Unicode FAQ for + Unix/Linux +
  8. +
+ + diff --git a/ext/utf8/source/utf8.h b/ext/utf8/source/utf8.h new file mode 100644 index 00000000..4e445140 --- /dev/null +++ b/ext/utf8/source/utf8.h @@ -0,0 +1,34 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include "utf8/checked.h" +#include "utf8/unchecked.h" + +#endif // header guard diff --git a/ext/utf8/source/utf8/checked.h b/ext/utf8/source/utf8/checked.h new file mode 100644 index 00000000..5670c196 --- /dev/null +++ b/ext/utf8/source/utf8/checked.h @@ -0,0 +1,318 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include "core.h" +#include + +namespace utf8 +{ + // Exceptions that may be thrown from the library functions. + class invalid_code_point : public std::exception { + uint32_t cp; + public: + invalid_code_point(uint32_t cp) : cp(cp) {} + virtual const char* what() const throw() { return "Invalid code point"; } + uint32_t code_point() const {return cp;} + }; + + class invalid_utf8 : public std::exception { + uint8_t u8; + public: + invalid_utf8 (uint8_t u) : u8(u) {} + virtual const char* what() const throw() { return "Invalid UTF-8"; } + uint8_t utf8_octet() const {return u8;} + }; + + class invalid_utf16 : public std::exception { + uint16_t u16; + public: + invalid_utf16 (uint16_t u) : u16(u) {} + virtual const char* what() const throw() { return "Invalid UTF-16"; } + uint16_t utf16_word() const {return u16;} + }; + + class not_enough_room : public std::exception { + public: + virtual const char* what() const throw() { return "Not enough space"; } + }; + + /// The library API - functions intended to be called by the users + + template + output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) + { + while (start != end) { + octet_iterator sequence_start = start; + internal::utf_error err_code = internal::validate_next(start, end); + switch (err_code) { + case internal::OK : + for (octet_iterator it = sequence_start; it != start; ++it) + *out++ = *it; + break; + case internal::NOT_ENOUGH_ROOM: + throw not_enough_room(); + case internal::INVALID_LEAD: + append (replacement, out); + ++start; + break; + case internal::INCOMPLETE_SEQUENCE: + case internal::OVERLONG_SEQUENCE: + case internal::INVALID_CODE_POINT: + append (replacement, out); + ++start; + // just one replacement mark for the sequence + while (internal::is_trail(*start) && start != end) + ++start; + break; + } + } + return out; + } + + template + inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) + { + static const uint32_t replacement_marker = internal::mask16(0xfffd); + return replace_invalid(start, end, out, replacement_marker); + } + + template + octet_iterator append(uint32_t cp, octet_iterator result) + { + if (!internal::is_code_point_valid(cp)) + throw invalid_code_point(cp); + + if (cp < 0x80) // one octet + *(result++) = static_cast(cp); + else if (cp < 0x800) { // two octets + *(result++) = static_cast((cp >> 6) | 0xc0); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + else if (cp < 0x10000) { // three octets + *(result++) = static_cast((cp >> 12) | 0xe0); + *(result++) = static_cast((cp >> 6) & 0x3f | 0x80); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + else if (cp <= internal::CODE_POINT_MAX) { // four octets + *(result++) = static_cast((cp >> 18) | 0xf0); + *(result++) = static_cast((cp >> 12)& 0x3f | 0x80); + *(result++) = static_cast((cp >> 6) & 0x3f | 0x80); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + else + throw invalid_code_point(cp); + + return result; + } + + template + uint32_t next(octet_iterator& it, octet_iterator end) + { + uint32_t cp = 0; + internal::utf_error err_code = internal::validate_next(it, end, &cp); + switch (err_code) { + case internal::OK : + break; + case internal::NOT_ENOUGH_ROOM : + throw not_enough_room(); + case internal::INVALID_LEAD : + case internal::INCOMPLETE_SEQUENCE : + case internal::OVERLONG_SEQUENCE : + throw invalid_utf8(*it); + case internal::INVALID_CODE_POINT : + throw invalid_code_point(cp); + } + return cp; + } + + template + uint32_t peek_next(octet_iterator it, octet_iterator end) + { + return next(it, end); + } + + template + uint32_t prior(octet_iterator& it, octet_iterator start) + { + octet_iterator end = it; + while (internal::is_trail(*(--it))) + if (it < start) + throw invalid_utf8(*it); // error - no lead byte in the sequence + octet_iterator temp = it; + return next(temp, end); + } + + /// Deprecated in versions that include "prior" + template + uint32_t previous(octet_iterator& it, octet_iterator pass_start) + { + octet_iterator end = it; + while (internal::is_trail(*(--it))) + if (it == pass_start) + throw invalid_utf8(*it); // error - no lead byte in the sequence + octet_iterator temp = it; + return next(temp, end); + } + + template + void advance (octet_iterator& it, distance_type n, octet_iterator end) + { + for (distance_type i = 0; i < n; ++i) + next(it, end); + } + + template + typename std::iterator_traits::difference_type + distance (octet_iterator first, octet_iterator last) + { + typename std::iterator_traits::difference_type dist; + for (dist = 0; first < last; ++dist) + next(first, last); + return dist; + } + + template + octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) + { + while (start != end) { + uint32_t cp = internal::mask16(*start++); + // Take care of surrogate pairs first + if (internal::is_surrogate(cp)) { + if (start != end) { + uint32_t trail_surrogate = internal::mask16(*start++); + if (trail_surrogate >= internal::TRAIL_SURROGATE_MIN && trail_surrogate <= internal::TRAIL_SURROGATE_MAX) + cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; + else + throw invalid_utf16(static_cast(trail_surrogate)); + } + else + throw invalid_utf16(static_cast(*start)); + + } + result = append(cp, result); + } + return result; + } + + template + u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) + { + while (start != end) { + uint32_t cp = next(start, end); + if (cp > 0xffff) { //make a surrogate pair + *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); + *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); + } + else + *result++ = static_cast(cp); + } + return result; + } + + template + octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) + { + while (start != end) + result = append(*(start++), result); + + return result; + } + + template + u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) + { + while (start < end) + (*result++) = next(start, end); + + return result; + } + + // The iterator class + template + class iterator : public std::iterator { + octet_iterator it; + octet_iterator range_start; + octet_iterator range_end; + public: + iterator () {}; + explicit iterator (const octet_iterator& octet_it, + const octet_iterator& range_start, + const octet_iterator& range_end) : + it(octet_it), range_start(range_start), range_end(range_end) + { + if (it < range_start || it > range_end) + throw std::out_of_range("Invalid utf-8 iterator position"); + } + // the default "big three" are OK + octet_iterator base () const { return it; } + uint32_t operator * () const + { + octet_iterator temp = it; + return next(temp, range_end); + } + bool operator == (const iterator& rhs) const + { + if (range_start != rhs.range_start || range_end != rhs.range_end) + throw std::logic_error("Comparing utf-8 iterators defined with different ranges"); + return (it == rhs.it); + } + bool operator != (const iterator& rhs) const + { + return !(operator == (rhs)); + } + iterator& operator ++ () + { + next(it, range_end); + return *this; + } + iterator operator ++ (int) + { + iterator temp = *this; + next(it, range_end); + return temp; + } + iterator& operator -- () + { + prior(it, range_start); + return *this; + } + iterator operator -- (int) + { + iterator temp = *this; + prior(it, range_start); + return temp; + } + }; // class iterator + +} // namespace utf8 + +#endif //header guard + + diff --git a/ext/utf8/source/utf8/core.h b/ext/utf8/source/utf8/core.h new file mode 100644 index 00000000..ca8c94a2 --- /dev/null +++ b/ext/utf8/source/utf8/core.h @@ -0,0 +1,259 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include + +namespace utf8 +{ + // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers + // You may need to change them to match your system. + // These typedefs have the same names as ones from cstdint, or boost/cstdint + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + +// Helper code - not intended to be directly called by the library users. May be changed at any time +namespace internal +{ + // Unicode constants + // Leading (high) surrogates: 0xd800 - 0xdbff + // Trailing (low) surrogates: 0xdc00 - 0xdfff + const uint16_t LEAD_SURROGATE_MIN = 0xd800u; + const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; + const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; + const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; + const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10); + const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN; + + // Maximum valid value for a Unicode code point + const uint32_t CODE_POINT_MAX = 0x0010ffffu; + + template + inline uint8_t mask8(octet_type oc) + { + return static_cast(0xff & oc); + } + template + inline uint16_t mask16(u16_type oc) + { + return static_cast(0xffff & oc); + } + template + inline bool is_trail(octet_type oc) + { + return ((mask8(oc) >> 6) == 0x2); + } + + template + inline bool is_surrogate(u16 cp) + { + return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); + } + + template + inline bool is_code_point_valid(u32 cp) + { + return (cp <= CODE_POINT_MAX && !is_surrogate(cp) && cp != 0xfffe && cp != 0xffff); + } + + template + inline typename std::iterator_traits::difference_type + sequence_length(octet_iterator lead_it) + { + uint8_t lead = mask8(*lead_it); + if (lead < 0x80) + return 1; + else if ((lead >> 5) == 0x6) + return 2; + else if ((lead >> 4) == 0xe) + return 3; + else if ((lead >> 3) == 0x1e) + return 4; + else + return 0; + } + + enum utf_error {OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT}; + + template + utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t* code_point) + { + uint32_t cp = mask8(*it); + // Check the lead octet + typedef typename std::iterator_traits::difference_type octet_difference_type; + octet_difference_type length = sequence_length(it); + + // "Shortcut" for ASCII characters + if (length == 1) { + if (end - it > 0) { + if (code_point) + *code_point = cp; + ++it; + return OK; + } + else + return NOT_ENOUGH_ROOM; + } + + // Do we have enough memory? + if (std::distance(it, end) < length) + return NOT_ENOUGH_ROOM; + + // Check trail octets and calculate the code point + switch (length) { + case 0: + return INVALID_LEAD; + break; + case 2: + if (is_trail(*(++it))) { + cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); + } + else { + --it; + return INCOMPLETE_SEQUENCE; + } + break; + case 3: + if (is_trail(*(++it))) { + cp = ((cp << 12) & 0xffff) + ((mask8(*it) << 6) & 0xfff); + if (is_trail(*(++it))) { + cp += (*it) & 0x3f; + } + else { + std::advance(it, -2); + return INCOMPLETE_SEQUENCE; + } + } + else { + --it; + return INCOMPLETE_SEQUENCE; + } + break; + case 4: + if (is_trail(*(++it))) { + cp = ((cp << 18) & 0x1fffff) + ((mask8(*it) << 12) & 0x3ffff); + if (is_trail(*(++it))) { + cp += (mask8(*it) << 6) & 0xfff; + if (is_trail(*(++it))) { + cp += (*it) & 0x3f; + } + else { + std::advance(it, -3); + return INCOMPLETE_SEQUENCE; + } + } + else { + std::advance(it, -2); + return INCOMPLETE_SEQUENCE; + } + } + else { + --it; + return INCOMPLETE_SEQUENCE; + } + break; + } + // Is the code point valid? + if (!is_code_point_valid(cp)) { + for (octet_difference_type i = 0; i < length - 1; ++i) + --it; + return INVALID_CODE_POINT; + } + + if (code_point) + *code_point = cp; + + if (cp < 0x80) { + if (length != 1) { + std::advance(it, -(length-1)); + return OVERLONG_SEQUENCE; + } + } + else if (cp < 0x800) { + if (length != 2) { + std::advance(it, -(length-1)); + return OVERLONG_SEQUENCE; + } + } + else if (cp < 0x10000) { + if (length != 3) { + std::advance(it, -(length-1)); + return OVERLONG_SEQUENCE; + } + } + + ++it; + return OK; + } + + template + inline utf_error validate_next(octet_iterator& it, octet_iterator end) { + return validate_next(it, end, 0); + } + +} // namespace internal + + /// The library API - functions intended to be called by the users + + // Byte order mark + const uint8_t bom[] = {0xef, 0xbb, 0xbf}; + + template + octet_iterator find_invalid(octet_iterator start, octet_iterator end) + { + octet_iterator result = start; + while (result != end) { + internal::utf_error err_code = internal::validate_next(result, end); + if (err_code != internal::OK) + return result; + } + return result; + } + + template + inline bool is_valid(octet_iterator start, octet_iterator end) + { + return (find_invalid(start, end) == end); + } + + template + inline bool is_bom (octet_iterator it) + { + return ( + (internal::mask8(*it++)) == bom[0] && + (internal::mask8(*it++)) == bom[1] && + (internal::mask8(*it)) == bom[2] + ); + } +} // namespace utf8 + +#endif // header guard + + diff --git a/ext/utf8/source/utf8/unchecked.h b/ext/utf8/source/utf8/unchecked.h new file mode 100644 index 00000000..4009cebe --- /dev/null +++ b/ext/utf8/source/utf8/unchecked.h @@ -0,0 +1,228 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include "core.h" + +namespace utf8 +{ + namespace unchecked + { + template + octet_iterator append(uint32_t cp, octet_iterator result) + { + if (cp < 0x80) // one octet + *(result++) = static_cast(cp); + else if (cp < 0x800) { // two octets + *(result++) = static_cast((cp >> 6) | 0xc0); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + else if (cp < 0x10000) { // three octets + *(result++) = static_cast((cp >> 12) | 0xe0); + *(result++) = static_cast((cp >> 6) & 0x3f | 0x80); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + else { // four octets + *(result++) = static_cast((cp >> 18) | 0xf0); + *(result++) = static_cast((cp >> 12)& 0x3f | 0x80); + *(result++) = static_cast((cp >> 6) & 0x3f | 0x80); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + return result; + } + + template + uint32_t next(octet_iterator& it) + { + uint32_t cp = internal::mask8(*it); + typename std::iterator_traits::difference_type length = utf8::internal::sequence_length(it); + switch (length) { + case 1: + break; + case 2: + it++; + cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); + break; + case 3: + ++it; + cp = ((cp << 12) & 0xffff) + ((internal::mask8(*it) << 6) & 0xfff); + ++it; + cp += (*it) & 0x3f; + break; + case 4: + ++it; + cp = ((cp << 18) & 0x1fffff) + ((internal::mask8(*it) << 12) & 0x3ffff); + ++it; + cp += (internal::mask8(*it) << 6) & 0xfff; + ++it; + cp += (*it) & 0x3f; + break; + } + ++it; + return cp; + } + + template + uint32_t peek_next(octet_iterator it) + { + return next(it); + } + + template + uint32_t prior(octet_iterator& it) + { + while (internal::is_trail(*(--it))) ; + octet_iterator temp = it; + return next(temp); + } + + // Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous) + template + inline uint32_t previous(octet_iterator& it) + { + return prior(it); + } + + template + void advance (octet_iterator& it, distance_type n) + { + for (distance_type i = 0; i < n; ++i) + next(it); + } + + template + typename std::iterator_traits::difference_type + distance (octet_iterator first, octet_iterator last) + { + typename std::iterator_traits::difference_type dist; + for (dist = 0; first < last; ++dist) + next(first); + return dist; + } + + template + octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) + { + while (start != end) { + uint32_t cp = internal::mask16(*start++); + // Take care of surrogate pairs first + if (internal::is_surrogate(cp)) { + uint32_t trail_surrogate = internal::mask16(*start++); + cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; + } + result = append(cp, result); + } + return result; + } + + template + u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) + { + while (start != end) { + uint32_t cp = next(start); + if (cp > 0xffff) { //make a surrogate pair + *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); + *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); + } + else + *result++ = static_cast(cp); + } + return result; + } + + template + octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) + { + while (start != end) + result = append(*(start++), result); + + return result; + } + + template + u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) + { + while (start < end) + (*result++) = next(start); + + return result; + } + + // The iterator class + template + class iterator : public std::iterator { + octet_iterator it; + public: + iterator () {}; + explicit iterator (const octet_iterator& octet_it): it(octet_it) {} + // the default "big three" are OK + octet_iterator base () const { return it; } + uint32_t operator * () const + { + octet_iterator temp = it; + return next(temp); + } + bool operator == (const iterator& rhs) const + { + return (it == rhs.it); + } + bool operator != (const iterator& rhs) const + { + return !(operator == (rhs)); + } + iterator& operator ++ () + { + std::advance(it, internal::sequence_length(it)); + return *this; + } + iterator operator ++ (int) + { + iterator temp = *this; + std::advance(it, internal::sequence_length(it)); + return temp; + } + iterator& operator -- () + { + prior(it); + return *this; + } + iterator operator -- (int) + { + iterator temp = *this; + prior(it); + return temp; + } + }; // class iterator + + } // namespace utf8::unchecked +} // namespace utf8 + + +#endif // header guard + diff --git a/ext/zlib/ChangeLog b/ext/zlib/ChangeLog new file mode 100644 index 00000000..1ac94630 --- /dev/null +++ b/ext/zlib/ChangeLog @@ -0,0 +1,855 @@ + + ChangeLog file for zlib + +Changes in 1.2.3 (18 July 2005) +- Apply security vulnerability fixes to contrib/infback9 as well +- Clean up some text files (carriage returns, trailing space) +- Update testzlib, vstudio, masmx64, and masmx86 in contrib [Vollant] + +Changes in 1.2.2.4 (11 July 2005) +- Add inflatePrime() function for starting inflation at bit boundary +- Avoid some Visual C warnings in deflate.c +- Avoid more silly Visual C warnings in inflate.c and inftrees.c for 64-bit + compile +- Fix some spelling errors in comments [Betts] +- Correct inflateInit2() error return documentation in zlib.h +- Added zran.c example of compressed data random access to examples + directory, shows use of inflatePrime() +- Fix cast for assignments to strm->state in inflate.c and infback.c +- Fix zlibCompileFlags() in zutil.c to use 1L for long shifts [Oberhumer] +- Move declarations of gf2 functions to right place in crc32.c [Oberhumer] +- Add cast in trees.c t avoid a warning [Oberhumer] +- Avoid some warnings in fitblk.c, gun.c, gzjoin.c in examples [Oberhumer] +- Update make_vms.com [Zinser] +- Initialize state->write in inflateReset() since copied in inflate_fast() +- Be more strict on incomplete code sets in inflate_table() and increase + ENOUGH and MAXD -- this repairs a possible security vulnerability for + invalid inflate input. Thanks to Tavis Ormandy and Markus Oberhumer for + discovering the vulnerability and providing test cases. +- Add ia64 support to configure for HP-UX [Smith] +- Add error return to gzread() for format or i/o error [Levin] +- Use malloc.h for OS/2 [Necasek] + +Changes in 1.2.2.3 (27 May 2005) +- Replace 1U constants in inflate.c and inftrees.c for 64-bit compile +- Typecast fread() return values in gzio.c [Vollant] +- Remove trailing space in minigzip.c outmode (VC++ can't deal with it) +- Fix crc check bug in gzread() after gzungetc() [Heiner] +- Add the deflateTune() function to adjust internal compression parameters +- Add a fast gzip decompressor, gun.c, to examples (use of inflateBack) +- Remove an incorrect assertion in examples/zpipe.c +- Add C++ wrapper in infback9.h [Donais] +- Fix bug in inflateCopy() when decoding fixed codes +- Note in zlib.h how much deflateSetDictionary() actually uses +- Remove USE_DICT_HEAD in deflate.c (would mess up inflate if used) +- Add _WIN32_WCE to define WIN32 in zconf.in.h [Spencer] +- Don't include stderr.h or errno.h for _WIN32_WCE in zutil.h [Spencer] +- Add gzdirect() function to indicate transparent reads +- Update contrib/minizip [Vollant] +- Fix compilation of deflate.c when both ASMV and FASTEST [Oberhumer] +- Add casts in crc32.c to avoid warnings [Oberhumer] +- Add contrib/masmx64 [Vollant] +- Update contrib/asm586, asm686, masmx86, testzlib, vstudio [Vollant] + +Changes in 1.2.2.2 (30 December 2004) +- Replace structure assignments in deflate.c and inflate.c with zmemcpy to + avoid implicit memcpy calls (portability for no-library compilation) +- Increase sprintf() buffer size in gzdopen() to allow for large numbers +- Add INFLATE_STRICT to check distances against zlib header +- Improve WinCE errno handling and comments [Chang] +- Remove comment about no gzip header processing in FAQ +- Add Z_FIXED strategy option to deflateInit2() to force fixed trees +- Add updated make_vms.com [Coghlan], update README +- Create a new "examples" directory, move gzappend.c there, add zpipe.c, + fitblk.c, gzlog.[ch], gzjoin.c, and zlib_how.html. +- Add FAQ entry and comments in deflate.c on uninitialized memory access +- Add Solaris 9 make options in configure [Gilbert] +- Allow strerror() usage in gzio.c for STDC +- Fix DecompressBuf in contrib/delphi/ZLib.pas [ManChesTer] +- Update contrib/masmx86/inffas32.asm and gvmat32.asm [Vollant] +- Use z_off_t for adler32_combine() and crc32_combine() lengths +- Make adler32() much faster for small len +- Use OS_CODE in deflate() default gzip header + +Changes in 1.2.2.1 (31 October 2004) +- Allow inflateSetDictionary() call for raw inflate +- Fix inflate header crc check bug for file names and comments +- Add deflateSetHeader() and gz_header structure for custom gzip headers +- Add inflateGetheader() to retrieve gzip headers +- Add crc32_combine() and adler32_combine() functions +- Add alloc_func, free_func, in_func, out_func to Z_PREFIX list +- Use zstreamp consistently in zlib.h (inflate_back functions) +- Remove GUNZIP condition from definition of inflate_mode in inflate.h + and in contrib/inflate86/inffast.S [Truta, Anderson] +- Add support for AMD64 in contrib/inflate86/inffas86.c [Anderson] +- Update projects/README.projects and projects/visualc6 [Truta] +- Update win32/DLL_FAQ.txt [Truta] +- Avoid warning under NO_GZCOMPRESS in gzio.c; fix typo [Truta] +- Deprecate Z_ASCII; use Z_TEXT instead [Truta] +- Use a new algorithm for setting strm->data_type in trees.c [Truta] +- Do not define an exit() prototype in zutil.c unless DEBUG defined +- Remove prototype of exit() from zutil.c, example.c, minigzip.c [Truta] +- Add comment in zlib.h for Z_NO_FLUSH parameter to deflate() +- Fix Darwin build version identification [Peterson] + +Changes in 1.2.2 (3 October 2004) +- Update zlib.h comments on gzip in-memory processing +- Set adler to 1 in inflateReset() to support Java test suite [Walles] +- Add contrib/dotzlib [Ravn] +- Update win32/DLL_FAQ.txt [Truta] +- Update contrib/minizip [Vollant] +- Move contrib/visual-basic.txt to old/ [Truta] +- Fix assembler builds in projects/visualc6/ [Truta] + +Changes in 1.2.1.2 (9 September 2004) +- Update INDEX file +- Fix trees.c to update strm->data_type (no one ever noticed!) +- Fix bug in error case in inflate.c, infback.c, and infback9.c [Brown] +- Add "volatile" to crc table flag declaration (for DYNAMIC_CRC_TABLE) +- Add limited multitasking protection to DYNAMIC_CRC_TABLE +- Add NO_vsnprintf for VMS in zutil.h [Mozilla] +- Don't declare strerror() under VMS [Mozilla] +- Add comment to DYNAMIC_CRC_TABLE to use get_crc_table() to initialize +- Update contrib/ada [Anisimkov] +- Update contrib/minizip [Vollant] +- Fix configure to not hardcode directories for Darwin [Peterson] +- Fix gzio.c to not return error on empty files [Brown] +- Fix indentation; update version in contrib/delphi/ZLib.pas and + contrib/pascal/zlibpas.pas [Truta] +- Update mkasm.bat in contrib/masmx86 [Truta] +- Update contrib/untgz [Truta] +- Add projects/README.projects [Truta] +- Add project for MS Visual C++ 6.0 in projects/visualc6 [Cadieux, Truta] +- Update win32/DLL_FAQ.txt [Truta] +- Update list of Z_PREFIX symbols in zconf.h [Randers-Pehrson, Truta] +- Remove an unnecessary assignment to curr in inftrees.c [Truta] +- Add OS/2 to exe builds in configure [Poltorak] +- Remove err dummy parameter in zlib.h [Kientzle] + +Changes in 1.2.1.1 (9 January 2004) +- Update email address in README +- Several FAQ updates +- Fix a big fat bug in inftrees.c that prevented decoding valid + dynamic blocks with only literals and no distance codes -- + Thanks to "Hot Emu" for the bug report and sample file +- Add a note to puff.c on no distance codes case. + +Changes in 1.2.1 (17 November 2003) +- Remove a tab in contrib/gzappend/gzappend.c +- Update some interfaces in contrib for new zlib functions +- Update zlib version number in some contrib entries +- Add Windows CE definition for ptrdiff_t in zutil.h [Mai, Truta] +- Support shared libraries on Hurd and KFreeBSD [Brown] +- Fix error in NO_DIVIDE option of adler32.c + +Changes in 1.2.0.8 (4 November 2003) +- Update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas +- Add experimental NO_DIVIDE #define in adler32.c + - Possibly faster on some processors (let me know if it is) +- Correct Z_BLOCK to not return on first inflate call if no wrap +- Fix strm->data_type on inflate() return to correctly indicate EOB +- Add deflatePrime() function for appending in the middle of a byte +- Add contrib/gzappend for an example of appending to a stream +- Update win32/DLL_FAQ.txt [Truta] +- Delete Turbo C comment in README [Truta] +- Improve some indentation in zconf.h [Truta] +- Fix infinite loop on bad input in configure script [Church] +- Fix gzeof() for concatenated gzip files [Johnson] +- Add example to contrib/visual-basic.txt [Michael B.] +- Add -p to mkdir's in Makefile.in [vda] +- Fix configure to properly detect presence or lack of printf functions +- Add AS400 support [Monnerat] +- Add a little Cygwin support [Wilson] + +Changes in 1.2.0.7 (21 September 2003) +- Correct some debug formats in contrib/infback9 +- Cast a type in a debug statement in trees.c +- Change search and replace delimiter in configure from % to # [Beebe] +- Update contrib/untgz to 0.2 with various fixes [Truta] +- Add build support for Amiga [Nikl] +- Remove some directories in old that have been updated to 1.2 +- Add dylib building for Mac OS X in configure and Makefile.in +- Remove old distribution stuff from Makefile +- Update README to point to DLL_FAQ.txt, and add comment on Mac OS X +- Update links in README + +Changes in 1.2.0.6 (13 September 2003) +- Minor FAQ updates +- Update contrib/minizip to 1.00 [Vollant] +- Remove test of gz functions in example.c when GZ_COMPRESS defined [Truta] +- Update POSTINC comment for 68060 [Nikl] +- Add contrib/infback9 with deflate64 decoding (unsupported) +- For MVS define NO_vsnprintf and undefine FAR [van Burik] +- Add pragma for fdopen on MVS [van Burik] + +Changes in 1.2.0.5 (8 September 2003) +- Add OF to inflateBackEnd() declaration in zlib.h +- Remember start when using gzdopen in the middle of a file +- Use internal off_t counters in gz* functions to properly handle seeks +- Perform more rigorous check for distance-too-far in inffast.c +- Add Z_BLOCK flush option to return from inflate at block boundary +- Set strm->data_type on return from inflate + - Indicate bits unused, if at block boundary, and if in last block +- Replace size_t with ptrdiff_t in crc32.c, and check for correct size +- Add condition so old NO_DEFLATE define still works for compatibility +- FAQ update regarding the Windows DLL [Truta] +- INDEX update: add qnx entry, remove aix entry [Truta] +- Install zlib.3 into mandir [Wilson] +- Move contrib/zlib_dll_FAQ.txt to win32/DLL_FAQ.txt; update [Truta] +- Adapt the zlib interface to the new DLL convention guidelines [Truta] +- Introduce ZLIB_WINAPI macro to allow the export of functions using + the WINAPI calling convention, for Visual Basic [Vollant, Truta] +- Update msdos and win32 scripts and makefiles [Truta] +- Export symbols by name, not by ordinal, in win32/zlib.def [Truta] +- Add contrib/ada [Anisimkov] +- Move asm files from contrib/vstudio/vc70_32 to contrib/asm386 [Truta] +- Rename contrib/asm386 to contrib/masmx86 [Truta, Vollant] +- Add contrib/masm686 [Truta] +- Fix offsets in contrib/inflate86 and contrib/masmx86/inffas32.asm + [Truta, Vollant] +- Update contrib/delphi; rename to contrib/pascal; add example [Truta] +- Remove contrib/delphi2; add a new contrib/delphi [Truta] +- Avoid inclusion of the nonstandard in contrib/iostream, + and fix some method prototypes [Truta] +- Fix the ZCR_SEED2 constant to avoid warnings in contrib/minizip + [Truta] +- Avoid the use of backslash (\) in contrib/minizip [Vollant] +- Fix file time handling in contrib/untgz; update makefiles [Truta] +- Update contrib/vstudio/vc70_32 to comply with the new DLL guidelines + [Vollant] +- Remove contrib/vstudio/vc15_16 [Vollant] +- Rename contrib/vstudio/vc70_32 to contrib/vstudio/vc7 [Truta] +- Update README.contrib [Truta] +- Invert the assignment order of match_head and s->prev[...] in + INSERT_STRING [Truta] +- Compare TOO_FAR with 32767 instead of 32768, to avoid 16-bit warnings + [Truta] +- Compare function pointers with 0, not with NULL or Z_NULL [Truta] +- Fix prototype of syncsearch in inflate.c [Truta] +- Introduce ASMINF macro to be enabled when using an ASM implementation + of inflate_fast [Truta] +- Change NO_DEFLATE to NO_GZCOMPRESS [Truta] +- Modify test_gzio in example.c to take a single file name as a + parameter [Truta] +- Exit the example.c program if gzopen fails [Truta] +- Add type casts around strlen in example.c [Truta] +- Remove casting to sizeof in minigzip.c; give a proper type + to the variable compared with SUFFIX_LEN [Truta] +- Update definitions of STDC and STDC99 in zconf.h [Truta] +- Synchronize zconf.h with the new Windows DLL interface [Truta] +- Use SYS16BIT instead of __32BIT__ to distinguish between + 16- and 32-bit platforms [Truta] +- Use far memory allocators in small 16-bit memory models for + Turbo C [Truta] +- Add info about the use of ASMV, ASMINF and ZLIB_WINAPI in + zlibCompileFlags [Truta] +- Cygwin has vsnprintf [Wilson] +- In Windows16, OS_CODE is 0, as in MSDOS [Truta] +- In Cygwin, OS_CODE is 3 (Unix), not 11 (Windows32) [Wilson] + +Changes in 1.2.0.4 (10 August 2003) +- Minor FAQ updates +- Be more strict when checking inflateInit2's windowBits parameter +- Change NO_GUNZIP compile option to NO_GZIP to cover deflate as well +- Add gzip wrapper option to deflateInit2 using windowBits +- Add updated QNX rule in configure and qnx directory [Bonnefoy] +- Make inflate distance-too-far checks more rigorous +- Clean up FAR usage in inflate +- Add casting to sizeof() in gzio.c and minigzip.c + +Changes in 1.2.0.3 (19 July 2003) +- Fix silly error in gzungetc() implementation [Vollant] +- Update contrib/minizip and contrib/vstudio [Vollant] +- Fix printf format in example.c +- Correct cdecl support in zconf.in.h [Anisimkov] +- Minor FAQ updates + +Changes in 1.2.0.2 (13 July 2003) +- Add ZLIB_VERNUM in zlib.h for numerical preprocessor comparisons +- Attempt to avoid warnings in crc32.c for pointer-int conversion +- Add AIX to configure, remove aix directory [Bakker] +- Add some casts to minigzip.c +- Improve checking after insecure sprintf() or vsprintf() calls +- Remove #elif's from crc32.c +- Change leave label to inf_leave in inflate.c and infback.c to avoid + library conflicts +- Remove inflate gzip decoding by default--only enable gzip decoding by + special request for stricter backward compatibility +- Add zlibCompileFlags() function to return compilation information +- More typecasting in deflate.c to avoid warnings +- Remove leading underscore from _Capital #defines [Truta] +- Fix configure to link shared library when testing +- Add some Windows CE target adjustments [Mai] +- Remove #define ZLIB_DLL in zconf.h [Vollant] +- Add zlib.3 [Rodgers] +- Update RFC URL in deflate.c and algorithm.txt [Mai] +- Add zlib_dll_FAQ.txt to contrib [Truta] +- Add UL to some constants [Truta] +- Update minizip and vstudio [Vollant] +- Remove vestigial NEED_DUMMY_RETURN from zconf.in.h +- Expand use of NO_DUMMY_DECL to avoid all dummy structures +- Added iostream3 to contrib [Schwardt] +- Replace rewind() with fseek() for WinCE [Truta] +- Improve setting of zlib format compression level flags + - Report 0 for huffman and rle strategies and for level == 0 or 1 + - Report 2 only for level == 6 +- Only deal with 64K limit when necessary at compile time [Truta] +- Allow TOO_FAR check to be turned off at compile time [Truta] +- Add gzclearerr() function [Souza] +- Add gzungetc() function + +Changes in 1.2.0.1 (17 March 2003) +- Add Z_RLE strategy for run-length encoding [Truta] + - When Z_RLE requested, restrict matches to distance one + - Update zlib.h, minigzip.c, gzopen(), gzdopen() for Z_RLE +- Correct FASTEST compilation to allow level == 0 +- Clean up what gets compiled for FASTEST +- Incorporate changes to zconf.in.h [Vollant] + - Refine detection of Turbo C need for dummy returns + - Refine ZLIB_DLL compilation + - Include additional header file on VMS for off_t typedef +- Try to use _vsnprintf where it supplants vsprintf [Vollant] +- Add some casts in inffast.c +- Enchance comments in zlib.h on what happens if gzprintf() tries to + write more than 4095 bytes before compression +- Remove unused state from inflateBackEnd() +- Remove exit(0) from minigzip.c, example.c +- Get rid of all those darn tabs +- Add "check" target to Makefile.in that does the same thing as "test" +- Add "mostlyclean" and "maintainer-clean" targets to Makefile.in +- Update contrib/inflate86 [Anderson] +- Update contrib/testzlib, contrib/vstudio, contrib/minizip [Vollant] +- Add msdos and win32 directories with makefiles [Truta] +- More additions and improvements to the FAQ + +Changes in 1.2.0 (9 March 2003) +- New and improved inflate code + - About 20% faster + - Does not allocate 32K window unless and until needed + - Automatically detects and decompresses gzip streams + - Raw inflate no longer needs an extra dummy byte at end + - Added inflateBack functions using a callback interface--even faster + than inflate, useful for file utilities (gzip, zip) + - Added inflateCopy() function to record state for random access on + externally generated deflate streams (e.g. in gzip files) + - More readable code (I hope) +- New and improved crc32() + - About 50% faster, thanks to suggestions from Rodney Brown +- Add deflateBound() and compressBound() functions +- Fix memory leak in deflateInit2() +- Permit setting dictionary for raw deflate (for parallel deflate) +- Fix const declaration for gzwrite() +- Check for some malloc() failures in gzio.c +- Fix bug in gzopen() on single-byte file 0x1f +- Fix bug in gzread() on concatenated file with 0x1f at end of buffer + and next buffer doesn't start with 0x8b +- Fix uncompress() to return Z_DATA_ERROR on truncated input +- Free memory at end of example.c +- Remove MAX #define in trees.c (conflicted with some libraries) +- Fix static const's in deflate.c, gzio.c, and zutil.[ch] +- Declare malloc() and free() in gzio.c if STDC not defined +- Use malloc() instead of calloc() in zutil.c if int big enough +- Define STDC for AIX +- Add aix/ with approach for compiling shared library on AIX +- Add HP-UX support for shared libraries in configure +- Add OpenUNIX support for shared libraries in configure +- Use $cc instead of gcc to build shared library +- Make prefix directory if needed when installing +- Correct Macintosh avoidance of typedef Byte in zconf.h +- Correct Turbo C memory allocation when under Linux +- Use libz.a instead of -lz in Makefile (assure use of compiled library) +- Update configure to check for snprintf or vsnprintf functions and their + return value, warn during make if using an insecure function +- Fix configure problem with compile-time knowledge of HAVE_UNISTD_H that + is lost when library is used--resolution is to build new zconf.h +- Documentation improvements (in zlib.h): + - Document raw deflate and inflate + - Update RFCs URL + - Point out that zlib and gzip formats are different + - Note that Z_BUF_ERROR is not fatal + - Document string limit for gzprintf() and possible buffer overflow + - Note requirement on avail_out when flushing + - Note permitted values of flush parameter of inflate() +- Add some FAQs (and even answers) to the FAQ +- Add contrib/inflate86/ for x86 faster inflate +- Add contrib/blast/ for PKWare Data Compression Library decompression +- Add contrib/puff/ simple inflate for deflate format description + +Changes in 1.1.4 (11 March 2002) +- ZFREE was repeated on same allocation on some error conditions. + This creates a security problem described in + http://www.zlib.org/advisory-2002-03-11.txt +- Returned incorrect error (Z_MEM_ERROR) on some invalid data +- Avoid accesses before window for invalid distances with inflate window + less than 32K. +- force windowBits > 8 to avoid a bug in the encoder for a window size + of 256 bytes. (A complete fix will be available in 1.1.5). + +Changes in 1.1.3 (9 July 1998) +- fix "an inflate input buffer bug that shows up on rare but persistent + occasions" (Mark) +- fix gzread and gztell for concatenated .gz files (Didier Le Botlan) +- fix gzseek(..., SEEK_SET) in write mode +- fix crc check after a gzeek (Frank Faubert) +- fix miniunzip when the last entry in a zip file is itself a zip file + (J Lillge) +- add contrib/asm586 and contrib/asm686 (Brian Raiter) + See http://www.muppetlabs.com/~breadbox/software/assembly.html +- add support for Delphi 3 in contrib/delphi (Bob Dellaca) +- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti) +- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren) +- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks) +- added a FAQ file + +- Support gzdopen on Mac with Metrowerks (Jason Linhart) +- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart) +- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young) +- avoid some warnings with Borland C (Tom Tanner) +- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant) +- emulate utime() for WIN32 in contrib/untgz (Gilles Vollant) +- allow several arguments to configure (Tim Mooney, Frodo Looijaard) +- use libdir and includedir in Makefile.in (Tim Mooney) +- support shared libraries on OSF1 V4 (Tim Mooney) +- remove so_locations in "make clean" (Tim Mooney) +- fix maketree.c compilation error (Glenn, Mark) +- Python interface to zlib now in Python 1.5 (Jeremy Hylton) +- new Makefile.riscos (Rich Walker) +- initialize static descriptors in trees.c for embedded targets (Nick Smith) +- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith) +- add the OS/2 files in Makefile.in too (Andrew Zabolotny) +- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane) +- fix maketree.c to allow clean compilation of inffixed.h (Mark) +- fix parameter check in deflateCopy (Gunther Nikl) +- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler) +- Many portability patches by Christian Spieler: + . zutil.c, zutil.h: added "const" for zmem* + . Make_vms.com: fixed some typos + . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists + . msdos/Makefile.msc: remove "default rtl link library" info from obj files + . msdos/Makefile.*: use model-dependent name for the built zlib library + . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc: + new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT) +- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane) +- replace __far with _far for better portability (Christian Spieler, Tom Lane) +- fix test for errno.h in configure (Tim Newsham) + +Changes in 1.1.2 (19 March 98) +- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant) + See http://www.winimage.com/zLibDll/unzip.html +- preinitialize the inflate tables for fixed codes, to make the code + completely thread safe (Mark) +- some simplifications and slight speed-up to the inflate code (Mark) +- fix gzeof on non-compressed files (Allan Schrum) +- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs) +- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn) +- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny) +- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori) +- do not wrap extern "C" around system includes (Tom Lane) +- mention zlib binding for TCL in README (Andreas Kupries) +- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert) +- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson) +- allow "configure --prefix $HOME" (Tim Mooney) +- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson) +- move Makefile.sas to amiga/Makefile.sas + +Changes in 1.1.1 (27 Feb 98) +- fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson) +- remove block truncation heuristic which had very marginal effect for zlib + (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the + compression ratio on some files. This also allows inlining _tr_tally for + matches in deflate_slow. +- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier) + +Changes in 1.1.0 (24 Feb 98) +- do not return STREAM_END prematurely in inflate (John Bowler) +- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler) +- compile with -DFASTEST to get compression code optimized for speed only +- in minigzip, try mmap'ing the input file first (Miguel Albrecht) +- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain + on Sun but significant on HP) + +- add a pointer to experimental unzip library in README (Gilles Vollant) +- initialize variable gcc in configure (Chris Herborth) + +Changes in 1.0.9 (17 Feb 1998) +- added gzputs and gzgets functions +- do not clear eof flag in gzseek (Mark Diekhans) +- fix gzseek for files in transparent mode (Mark Diekhans) +- do not assume that vsprintf returns the number of bytes written (Jens Krinke) +- replace EXPORT with ZEXPORT to avoid conflict with other programs +- added compress2 in zconf.h, zlib.def, zlib.dnt +- new asm code from Gilles Vollant in contrib/asm386 +- simplify the inflate code (Mark): + . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new() + . ZALLOC the length list in inflate_trees_fixed() instead of using stack + . ZALLOC the value area for huft_build() instead of using stack + . Simplify Z_FINISH check in inflate() + +- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8 +- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi) +- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with + the declaration of FAR (Gilles VOllant) +- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann) +- read_buf buf parameter of type Bytef* instead of charf* +- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout) +- do not redeclare unlink in minigzip.c for WIN32 (John Bowler) +- fix check for presence of directories in "make install" (Ian Willis) + +Changes in 1.0.8 (27 Jan 1998) +- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant) +- fix gzgetc and gzputc for big endian systems (Markus Oberhumer) +- added compress2() to allow setting the compression level +- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong) +- use constant arrays for the static trees in trees.c instead of computing + them at run time (thanks to Ken Raeburn for this suggestion). To create + trees.h, compile with GEN_TREES_H and run "make test". +- check return code of example in "make test" and display result +- pass minigzip command line options to file_compress +- simplifying code of inflateSync to avoid gcc 2.8 bug + +- support CC="gcc -Wall" in configure -s (QingLong) +- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn) +- fix test for shared library support to avoid compiler warnings +- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant) +- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit) +- do not use fdopen for Metrowerks on Mac (Brad Pettit)) +- add checks for gzputc and gzputc in example.c +- avoid warnings in gzio.c and deflate.c (Andreas Kleinert) +- use const for the CRC table (Ken Raeburn) +- fixed "make uninstall" for shared libraries +- use Tracev instead of Trace in infblock.c +- in example.c use correct compressed length for test_sync +- suppress +vnocompatwarnings in configure for HPUX (not always supported) + +Changes in 1.0.7 (20 Jan 1998) +- fix gzseek which was broken in write mode +- return error for gzseek to negative absolute position +- fix configure for Linux (Chun-Chung Chen) +- increase stack space for MSC (Tim Wegner) +- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant) +- define EXPORTVA for gzprintf (Gilles Vollant) +- added man page zlib.3 (Rick Rodgers) +- for contrib/untgz, fix makedir() and improve Makefile + +- check gzseek in write mode in example.c +- allocate extra buffer for seeks only if gzseek is actually called +- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant) +- add inflateSyncPoint in zconf.h +- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def + +Changes in 1.0.6 (19 Jan 1998) +- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and + gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code) +- Fix a deflate bug occurring only with compression level 0 (thanks to + Andy Buckler for finding this one). +- In minigzip, pass transparently also the first byte for .Z files. +- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress() +- check Z_FINISH in inflate (thanks to Marc Schluper) +- Implement deflateCopy (thanks to Adam Costello) +- make static libraries by default in configure, add --shared option. +- move MSDOS or Windows specific files to directory msdos +- suppress the notion of partial flush to simplify the interface + (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4) +- suppress history buffer provided by application to simplify the interface + (this feature was not implemented anyway in 1.0.4) +- next_in and avail_in must be initialized before calling inflateInit or + inflateInit2 +- add EXPORT in all exported functions (for Windows DLL) +- added Makefile.nt (thanks to Stephen Williams) +- added the unsupported "contrib" directory: + contrib/asm386/ by Gilles Vollant + 386 asm code replacing longest_match(). + contrib/iostream/ by Kevin Ruland + A C++ I/O streams interface to the zlib gz* functions + contrib/iostream2/ by Tyge Løvset + Another C++ I/O streams interface + contrib/untgz/ by "Pedro A. Aranda Guti\irrez" + A very simple tar.gz file extractor using zlib + contrib/visual-basic.txt by Carlos Rios + How to use compress(), uncompress() and the gz* functions from VB. +- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression + level) in minigzip (thanks to Tom Lane) + +- use const for rommable constants in deflate +- added test for gzseek and gztell in example.c +- add undocumented function inflateSyncPoint() (hack for Paul Mackerras) +- add undocumented function zError to convert error code to string + (for Tim Smithers) +- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code. +- Use default memcpy for Symantec MSDOS compiler. +- Add EXPORT keyword for check_func (needed for Windows DLL) +- add current directory to LD_LIBRARY_PATH for "make test" +- create also a link for libz.so.1 +- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura) +- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX) +- added -soname for Linux in configure (Chun-Chung Chen, +- assign numbers to the exported functions in zlib.def (for Windows DLL) +- add advice in zlib.h for best usage of deflateSetDictionary +- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn) +- allow compilation with ANSI keywords only enabled for TurboC in large model +- avoid "versionString"[0] (Borland bug) +- add NEED_DUMMY_RETURN for Borland +- use variable z_verbose for tracing in debug mode (L. Peter Deutsch). +- allow compilation with CC +- defined STDC for OS/2 (David Charlap) +- limit external names to 8 chars for MVS (Thomas Lund) +- in minigzip.c, use static buffers only for 16-bit systems +- fix suffix check for "minigzip -d foo.gz" +- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee) +- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau) +- added makelcc.bat for lcc-win32 (Tom St Denis) +- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe) +- Avoid expanded $Id$. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion. +- check for unistd.h in configure (for off_t) +- remove useless check parameter in inflate_blocks_free +- avoid useless assignment of s->check to itself in inflate_blocks_new +- do not flush twice in gzclose (thanks to Ken Raeburn) +- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h +- use NO_ERRNO_H instead of enumeration of operating systems with errno.h +- work around buggy fclose on pipes for HP/UX +- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson) +- fix configure if CC is already equal to gcc + +Changes in 1.0.5 (3 Jan 98) +- Fix inflate to terminate gracefully when fed corrupted or invalid data +- Use const for rommable constants in inflate +- Eliminate memory leaks on error conditions in inflate +- Removed some vestigial code in inflate +- Update web address in README + +Changes in 1.0.4 (24 Jul 96) +- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF + bit, so the decompressor could decompress all the correct data but went + on to attempt decompressing extra garbage data. This affected minigzip too. +- zlibVersion and gzerror return const char* (needed for DLL) +- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno) +- use z_error only for DEBUG (avoid problem with DLLs) + +Changes in 1.0.3 (2 Jul 96) +- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS + small and medium models; this makes the library incompatible with previous + versions for these models. (No effect in large model or on other systems.) +- return OK instead of BUF_ERROR if previous deflate call returned with + avail_out as zero but there is nothing to do +- added memcmp for non STDC compilers +- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly) +- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO) +- better check for 16-bit mode MSC (avoids problem with Symantec) + +Changes in 1.0.2 (23 May 96) +- added Windows DLL support +- added a function zlibVersion (for the DLL support) +- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model) +- Bytef is define's instead of typedef'd only for Borland C +- avoid reading uninitialized memory in example.c +- mention in README that the zlib format is now RFC1950 +- updated Makefile.dj2 +- added algorithm.doc + +Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion] +- fix array overlay in deflate.c which sometimes caused bad compressed data +- fix inflate bug with empty stored block +- fix MSDOS medium model which was broken in 0.99 +- fix deflateParams() which could generated bad compressed data. +- Bytef is define'd instead of typedef'ed (work around Borland bug) +- added an INDEX file +- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32), + Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas) +- speed up adler32 for modern machines without auto-increment +- added -ansi for IRIX in configure +- static_init_done in trees.c is an int +- define unlink as delete for VMS +- fix configure for QNX +- add configure branch for SCO and HPUX +- avoid many warnings (unused variables, dead assignments, etc...) +- no fdopen for BeOS +- fix the Watcom fix for 32 bit mode (define FAR as empty) +- removed redefinition of Byte for MKWERKS +- work around an MWKERKS bug (incorrect merge of all .h files) + +Changes in 0.99 (27 Jan 96) +- allow preset dictionary shared between compressor and decompressor +- allow compression level 0 (no compression) +- add deflateParams in zlib.h: allow dynamic change of compression level + and compression strategy. +- test large buffers and deflateParams in example.c +- add optional "configure" to build zlib as a shared library +- suppress Makefile.qnx, use configure instead +- fixed deflate for 64-bit systems (detected on Cray) +- fixed inflate_blocks for 64-bit systems (detected on Alpha) +- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2) +- always return Z_BUF_ERROR when deflate() has nothing to do +- deflateInit and inflateInit are now macros to allow version checking +- prefix all global functions and types with z_ with -DZ_PREFIX +- make falloc completely reentrant (inftrees.c) +- fixed very unlikely race condition in ct_static_init +- free in reverse order of allocation to help memory manager +- use zlib-1.0/* instead of zlib/* inside the tar.gz +- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith + -Wconversion -Wstrict-prototypes -Wmissing-prototypes" +- allow gzread on concatenated .gz files +- deflateEnd now returns Z_DATA_ERROR if it was premature +- deflate is finally (?) fully deterministic (no matches beyond end of input) +- Document Z_SYNC_FLUSH +- add uninstall in Makefile +- Check for __cpluplus in zlib.h +- Better test in ct_align for partial flush +- avoid harmless warnings for Borland C++ +- initialize hash_head in deflate.c +- avoid warning on fdopen (gzio.c) for HP cc -Aa +- include stdlib.h for STDC compilers +- include errno.h for Cray +- ignore error if ranlib doesn't exist +- call ranlib twice for NeXTSTEP +- use exec_prefix instead of prefix for libz.a +- renamed ct_* as _tr_* to avoid conflict with applications +- clear z->msg in inflateInit2 before any error return +- initialize opaque in example.c, gzio.c, deflate.c and inflate.c +- fixed typo in zconf.h (_GNUC__ => __GNUC__) +- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode) +- fix typo in Make_vms.com (f$trnlnm -> f$getsyi) +- in fcalloc, normalize pointer if size > 65520 bytes +- don't use special fcalloc for 32 bit Borland C++ +- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc... +- use Z_BINARY instead of BINARY +- document that gzclose after gzdopen will close the file +- allow "a" as mode in gzopen. +- fix error checking in gzread +- allow skipping .gz extra-field on pipes +- added reference to Perl interface in README +- put the crc table in FAR data (I dislike more and more the medium model :) +- added get_crc_table +- added a dimension to all arrays (Borland C can't count). +- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast +- guard against multiple inclusion of *.h (for precompiled header on Mac) +- Watcom C pretends to be Microsoft C small model even in 32 bit mode. +- don't use unsized arrays to avoid silly warnings by Visual C++: + warning C4746: 'inflate_mask' : unsized array treated as '__far' + (what's wrong with far data in far model?). +- define enum out of inflate_blocks_state to allow compilation with C++ + +Changes in 0.95 (16 Aug 95) +- fix MSDOS small and medium model (now easier to adapt to any compiler) +- inlined send_bits +- fix the final (:-) bug for deflate with flush (output was correct but + not completely flushed in rare occasions). +- default window size is same for compression and decompression + (it's now sufficient to set MAX_WBITS in zconf.h). +- voidp -> voidpf and voidnp -> voidp (for consistency with other + typedefs and because voidnp was not near in large model). + +Changes in 0.94 (13 Aug 95) +- support MSDOS medium model +- fix deflate with flush (could sometimes generate bad output) +- fix deflateReset (zlib header was incorrectly suppressed) +- added support for VMS +- allow a compression level in gzopen() +- gzflush now calls fflush +- For deflate with flush, flush even if no more input is provided. +- rename libgz.a as libz.a +- avoid complex expression in infcodes.c triggering Turbo C bug +- work around a problem with gcc on Alpha (in INSERT_STRING) +- don't use inline functions (problem with some gcc versions) +- allow renaming of Byte, uInt, etc... with #define. +- avoid warning about (unused) pointer before start of array in deflate.c +- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c +- avoid reserved word 'new' in trees.c + +Changes in 0.93 (25 June 95) +- temporarily disable inline functions +- make deflate deterministic +- give enough lookahead for PARTIAL_FLUSH +- Set binary mode for stdin/stdout in minigzip.c for OS/2 +- don't even use signed char in inflate (not portable enough) +- fix inflate memory leak for segmented architectures + +Changes in 0.92 (3 May 95) +- don't assume that char is signed (problem on SGI) +- Clear bit buffer when starting a stored block +- no memcpy on Pyramid +- suppressed inftest.c +- optimized fill_window, put longest_match inline for gcc +- optimized inflate on stored blocks. +- untabify all sources to simplify patches + +Changes in 0.91 (2 May 95) +- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h +- Document the memory requirements in zconf.h +- added "make install" +- fix sync search logic in inflateSync +- deflate(Z_FULL_FLUSH) now works even if output buffer too short +- after inflateSync, don't scare people with just "lo world" +- added support for DJGPP + +Changes in 0.9 (1 May 95) +- don't assume that zalloc clears the allocated memory (the TurboC bug + was Mark's bug after all :) +- let again gzread copy uncompressed data unchanged (was working in 0.71) +- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented +- added a test of inflateSync in example.c +- moved MAX_WBITS to zconf.h because users might want to change that. +- document explicitly that zalloc(64K) on MSDOS must return a normalized + pointer (zero offset) +- added Makefiles for Microsoft C, Turbo C, Borland C++ +- faster crc32() + +Changes in 0.8 (29 April 95) +- added fast inflate (inffast.c) +- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this + is incompatible with previous versions of zlib which returned Z_OK. +- work around a TurboC compiler bug (bad code for b << 0, see infutil.h) + (actually that was not a compiler bug, see 0.81 above) +- gzread no longer reads one extra byte in certain cases +- In gzio destroy(), don't reference a freed structure +- avoid many warnings for MSDOS +- avoid the ERROR symbol which is used by MS Windows + +Changes in 0.71 (14 April 95) +- Fixed more MSDOS compilation problems :( There is still a bug with + TurboC large model. + +Changes in 0.7 (14 April 95) +- Added full inflate support. +- Simplified the crc32() interface. The pre- and post-conditioning + (one's complement) is now done inside crc32(). WARNING: this is + incompatible with previous versions; see zlib.h for the new usage. + +Changes in 0.61 (12 April 95) +- workaround for a bug in TurboC. example and minigzip now work on MSDOS. + +Changes in 0.6 (11 April 95) +- added minigzip.c +- added gzdopen to reopen a file descriptor as gzFile +- added transparent reading of non-gziped files in gzread. +- fixed bug in gzread (don't read crc as data) +- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose). +- don't allocate big arrays in the stack (for MSDOS) +- fix some MSDOS compilation problems + +Changes in 0.5: +- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but + not yet Z_FULL_FLUSH. +- support decompression but only in a single step (forced Z_FINISH) +- added opaque object for zalloc and zfree. +- added deflateReset and inflateReset +- added a variable zlib_version for consistency checking. +- renamed the 'filter' parameter of deflateInit2 as 'strategy'. + Added Z_FILTERED and Z_HUFFMAN_ONLY constants. + +Changes in 0.4: +- avoid "zip" everywhere, use zlib instead of ziplib. +- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush + if compression method == 8. +- added adler32 and crc32 +- renamed deflateOptions as deflateInit2, call one or the other but not both +- added the method parameter for deflateInit2. +- added inflateInit2 +- simplied considerably deflateInit and inflateInit by not supporting + user-provided history buffer. This is supported only in deflateInit2 + and inflateInit2. + +Changes in 0.3: +- prefix all macro names with Z_ +- use Z_FINISH instead of deflateEnd to finish compression. +- added Z_HUFFMAN_ONLY +- added gzerror() diff --git a/ext/zlib/FAQ b/ext/zlib/FAQ new file mode 100644 index 00000000..15d04361 --- /dev/null +++ b/ext/zlib/FAQ @@ -0,0 +1,339 @@ + + Frequently Asked Questions about zlib + + +If your question is not there, please check the zlib home page +http://www.zlib.org which may have more recent information. +The lastest zlib FAQ is at http://www.gzip.org/zlib/zlib_faq.html + + + 1. Is zlib Y2K-compliant? + + Yes. zlib doesn't handle dates. + + 2. Where can I get a Windows DLL version? + + The zlib sources can be compiled without change to produce a DLL. + See the file win32/DLL_FAQ.txt in the zlib distribution. + Pointers to the precompiled DLL are found in the zlib web site at + http://www.zlib.org. + + 3. Where can I get a Visual Basic interface to zlib? + + See + * http://www.dogma.net/markn/articles/zlibtool/zlibtool.htm + * contrib/visual-basic.txt in the zlib distribution + * win32/DLL_FAQ.txt in the zlib distribution + + 4. compress() returns Z_BUF_ERROR. + + Make sure that before the call of compress, the length of the compressed + buffer is equal to the total size of the compressed buffer and not + zero. For Visual Basic, check that this parameter is passed by reference + ("as any"), not by value ("as long"). + + 5. deflate() or inflate() returns Z_BUF_ERROR. + + Before making the call, make sure that avail_in and avail_out are not + zero. When setting the parameter flush equal to Z_FINISH, also make sure + that avail_out is big enough to allow processing all pending input. + Note that a Z_BUF_ERROR is not fatal--another call to deflate() or + inflate() can be made with more input or output space. A Z_BUF_ERROR + may in fact be unavoidable depending on how the functions are used, since + it is not possible to tell whether or not there is more output pending + when strm.avail_out returns with zero. + + 6. Where's the zlib documentation (man pages, etc.)? + + It's in zlib.h for the moment, and Francis S. Lin has converted it to a + web page zlib.html. Volunteers to transform this to Unix-style man pages, + please contact us (zlib@gzip.org). Examples of zlib usage are in the files + example.c and minigzip.c. + + 7. Why don't you use GNU autoconf or libtool or ...? + + Because we would like to keep zlib as a very small and simple + package. zlib is rather portable and doesn't need much configuration. + + 8. I found a bug in zlib. + + Most of the time, such problems are due to an incorrect usage of + zlib. Please try to reproduce the problem with a small program and send + the corresponding source to us at zlib@gzip.org . Do not send + multi-megabyte data files without prior agreement. + + 9. Why do I get "undefined reference to gzputc"? + + If "make test" produces something like + + example.o(.text+0x154): undefined reference to `gzputc' + + check that you don't have old files libz.* in /usr/lib, /usr/local/lib or + /usr/X11R6/lib. Remove any old versions, then do "make install". + +10. I need a Delphi interface to zlib. + + See the contrib/delphi directory in the zlib distribution. + +11. Can zlib handle .zip archives? + + Not by itself, no. See the directory contrib/minizip in the zlib + distribution. + +12. Can zlib handle .Z files? + + No, sorry. You have to spawn an uncompress or gunzip subprocess, or adapt + the code of uncompress on your own. + +13. How can I make a Unix shared library? + + make clean + ./configure -s + make + +14. How do I install a shared zlib library on Unix? + + After the above, then: + + make install + + However, many flavors of Unix come with a shared zlib already installed. + Before going to the trouble of compiling a shared version of zlib and + trying to install it, you may want to check if it's already there! If you + can #include , it's there. The -lz option will probably link to it. + +15. I have a question about OttoPDF. + + We are not the authors of OttoPDF. The real author is on the OttoPDF web + site: Joel Hainley, jhainley@myndkryme.com. + +16. Can zlib decode Flate data in an Adobe PDF file? + + Yes. See http://www.fastio.com/ (ClibPDF), or http://www.pdflib.com/ . + To modify PDF forms, see http://sourceforge.net/projects/acroformtool/ . + +17. Why am I getting this "register_frame_info not found" error on Solaris? + + After installing zlib 1.1.4 on Solaris 2.6, running applications using zlib + generates an error such as: + + ld.so.1: rpm: fatal: relocation error: file /usr/local/lib/libz.so: + symbol __register_frame_info: referenced symbol not found + + The symbol __register_frame_info is not part of zlib, it is generated by + the C compiler (cc or gcc). You must recompile applications using zlib + which have this problem. This problem is specific to Solaris. See + http://www.sunfreeware.com for Solaris versions of zlib and applications + using zlib. + +18. Why does gzip give an error on a file I make with compress/deflate? + + The compress and deflate functions produce data in the zlib format, which + is different and incompatible with the gzip format. The gz* functions in + zlib on the other hand use the gzip format. Both the zlib and gzip + formats use the same compressed data format internally, but have different + headers and trailers around the compressed data. + +19. Ok, so why are there two different formats? + + The gzip format was designed to retain the directory information about + a single file, such as the name and last modification date. The zlib + format on the other hand was designed for in-memory and communication + channel applications, and has a much more compact header and trailer and + uses a faster integrity check than gzip. + +20. Well that's nice, but how do I make a gzip file in memory? + + You can request that deflate write the gzip format instead of the zlib + format using deflateInit2(). You can also request that inflate decode + the gzip format using inflateInit2(). Read zlib.h for more details. + +21. Is zlib thread-safe? + + Yes. However any library routines that zlib uses and any application- + provided memory allocation routines must also be thread-safe. zlib's gz* + functions use stdio library routines, and most of zlib's functions use the + library memory allocation routines by default. zlib's Init functions allow + for the application to provide custom memory allocation routines. + + Of course, you should only operate on any given zlib or gzip stream from a + single thread at a time. + +22. Can I use zlib in my commercial application? + + Yes. Please read the license in zlib.h. + +23. Is zlib under the GNU license? + + No. Please read the license in zlib.h. + +24. The license says that altered source versions must be "plainly marked". So + what exactly do I need to do to meet that requirement? + + You need to change the ZLIB_VERSION and ZLIB_VERNUM #defines in zlib.h. In + particular, the final version number needs to be changed to "f", and an + identification string should be appended to ZLIB_VERSION. Version numbers + x.x.x.f are reserved for modifications to zlib by others than the zlib + maintainers. For example, if the version of the base zlib you are altering + is "1.2.3.4", then in zlib.h you should change ZLIB_VERNUM to 0x123f, and + ZLIB_VERSION to something like "1.2.3.f-zachary-mods-v3". You can also + update the version strings in deflate.c and inftrees.c. + + For altered source distributions, you should also note the origin and + nature of the changes in zlib.h, as well as in ChangeLog and README, along + with the dates of the alterations. The origin should include at least your + name (or your company's name), and an email address to contact for help or + issues with the library. + + Note that distributing a compiled zlib library along with zlib.h and + zconf.h is also a source distribution, and so you should change + ZLIB_VERSION and ZLIB_VERNUM and note the origin and nature of the changes + in zlib.h as you would for a full source distribution. + +25. Will zlib work on a big-endian or little-endian architecture, and can I + exchange compressed data between them? + + Yes and yes. + +26. Will zlib work on a 64-bit machine? + + It should. It has been tested on 64-bit machines, and has no dependence + on any data types being limited to 32-bits in length. If you have any + difficulties, please provide a complete problem report to zlib@gzip.org + +27. Will zlib decompress data from the PKWare Data Compression Library? + + No. The PKWare DCL uses a completely different compressed data format + than does PKZIP and zlib. However, you can look in zlib's contrib/blast + directory for a possible solution to your problem. + +28. Can I access data randomly in a compressed stream? + + No, not without some preparation. If when compressing you periodically + use Z_FULL_FLUSH, carefully write all the pending data at those points, + and keep an index of those locations, then you can start decompression + at those points. You have to be careful to not use Z_FULL_FLUSH too + often, since it can significantly degrade compression. + +29. Does zlib work on MVS, OS/390, CICS, etc.? + + We don't know for sure. We have heard occasional reports of success on + these systems. If you do use it on one of these, please provide us with + a report, instructions, and patches that we can reference when we get + these questions. Thanks. + +30. Is there some simpler, easier to read version of inflate I can look at + to understand the deflate format? + + First off, you should read RFC 1951. Second, yes. Look in zlib's + contrib/puff directory. + +31. Does zlib infringe on any patents? + + As far as we know, no. In fact, that was originally the whole point behind + zlib. Look here for some more information: + + http://www.gzip.org/#faq11 + +32. Can zlib work with greater than 4 GB of data? + + Yes. inflate() and deflate() will process any amount of data correctly. + Each call of inflate() or deflate() is limited to input and output chunks + of the maximum value that can be stored in the compiler's "unsigned int" + type, but there is no limit to the number of chunks. Note however that the + strm.total_in and strm_total_out counters may be limited to 4 GB. These + counters are provided as a convenience and are not used internally by + inflate() or deflate(). The application can easily set up its own counters + updated after each call of inflate() or deflate() to count beyond 4 GB. + compress() and uncompress() may be limited to 4 GB, since they operate in a + single call. gzseek() and gztell() may be limited to 4 GB depending on how + zlib is compiled. See the zlibCompileFlags() function in zlib.h. + + The word "may" appears several times above since there is a 4 GB limit + only if the compiler's "long" type is 32 bits. If the compiler's "long" + type is 64 bits, then the limit is 16 exabytes. + +33. Does zlib have any security vulnerabilities? + + The only one that we are aware of is potentially in gzprintf(). If zlib + is compiled to use sprintf() or vsprintf(), then there is no protection + against a buffer overflow of a 4K string space, other than the caller of + gzprintf() assuring that the output will not exceed 4K. On the other + hand, if zlib is compiled to use snprintf() or vsnprintf(), which should + normally be the case, then there is no vulnerability. The ./configure + script will display warnings if an insecure variation of sprintf() will + be used by gzprintf(). Also the zlibCompileFlags() function will return + information on what variant of sprintf() is used by gzprintf(). + + If you don't have snprintf() or vsnprintf() and would like one, you can + find a portable implementation here: + + http://www.ijs.si/software/snprintf/ + + Note that you should be using the most recent version of zlib. Versions + 1.1.3 and before were subject to a double-free vulnerability. + +34. Is there a Java version of zlib? + + Probably what you want is to use zlib in Java. zlib is already included + as part of the Java SDK in the java.util.zip package. If you really want + a version of zlib written in the Java language, look on the zlib home + page for links: http://www.zlib.org/ + +35. I get this or that compiler or source-code scanner warning when I crank it + up to maximally-pedantic. Can't you guys write proper code? + + Many years ago, we gave up attempting to avoid warnings on every compiler + in the universe. It just got to be a waste of time, and some compilers + were downright silly. So now, we simply make sure that the code always + works. + +36. Valgrind (or some similar memory access checker) says that deflate is + performing a conditional jump that depends on an uninitialized value. + Isn't that a bug? + + No. That is intentional for performance reasons, and the output of + deflate is not affected. This only started showing up recently since + zlib 1.2.x uses malloc() by default for allocations, whereas earlier + versions used calloc(), which zeros out the allocated memory. + +37. Will zlib read the (insert any ancient or arcane format here) compressed + data format? + + Probably not. Look in the comp.compression FAQ for pointers to various + formats and associated software. + +38. How can I encrypt/decrypt zip files with zlib? + + zlib doesn't support encryption. The original PKZIP encryption is very weak + and can be broken with freely available programs. To get strong encryption, + use GnuPG, http://www.gnupg.org/ , which already includes zlib compression. + For PKZIP compatible "encryption", look at http://www.info-zip.org/ + +39. What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings? + + "gzip" is the gzip format, and "deflate" is the zlib format. They should + probably have called the second one "zlib" instead to avoid confusion + with the raw deflate compressed data format. While the HTTP 1.1 RFC 2616 + correctly points to the zlib specification in RFC 1950 for the "deflate" + transfer encoding, there have been reports of servers and browsers that + incorrectly produce or expect raw deflate data per the deflate + specficiation in RFC 1951, most notably Microsoft. So even though the + "deflate" transfer encoding using the zlib format would be the more + efficient approach (and in fact exactly what the zlib format was designed + for), using the "gzip" transfer encoding is probably more reliable due to + an unfortunate choice of name on the part of the HTTP 1.1 authors. + + Bottom line: use the gzip format for HTTP 1.1 encoding. + +40. Does zlib support the new "Deflate64" format introduced by PKWare? + + No. PKWare has apparently decided to keep that format proprietary, since + they have not documented it as they have previous compression formats. + In any case, the compression improvements are so modest compared to other + more modern approaches, that it's not worth the effort to implement. + +41. Can you please sign these lengthy legal documents and fax them back to us + so that we can use your software in our product? + + No. Go away. Shoo. diff --git a/ext/zlib/INDEX b/ext/zlib/INDEX new file mode 100644 index 00000000..4d7eac44 --- /dev/null +++ b/ext/zlib/INDEX @@ -0,0 +1,51 @@ +ChangeLog history of changes +FAQ Frequently Asked Questions about zlib +INDEX this file +Makefile makefile for Unix (generated by configure) +Makefile.in makefile for Unix (template for configure) +README guess what +algorithm.txt description of the (de)compression algorithm +configure configure script for Unix +zconf.in.h template for zconf.h (used by configure) + +amiga/ makefiles for Amiga SAS C +as400/ makefiles for IBM AS/400 +msdos/ makefiles for MSDOS +old/ makefiles for various architectures and zlib documentation + files that have not yet been updated for zlib 1.2.x +projects/ projects for various Integrated Development Environments +qnx/ makefiles for QNX +win32/ makefiles for Windows + + zlib public header files (must be kept): +zconf.h +zlib.h + + private source files used to build the zlib library: +adler32.c +compress.c +crc32.c +crc32.h +deflate.c +deflate.h +gzio.c +infback.c +inffast.c +inffast.h +inffixed.h +inflate.c +inflate.h +inftrees.c +inftrees.h +trees.c +trees.h +uncompr.c +zutil.c +zutil.h + + source files for sample programs: +example.c +minigzip.c + + unsupported contribution by third parties +See contrib/README.contrib diff --git a/ext/zlib/README b/ext/zlib/README new file mode 100644 index 00000000..80f71ae8 --- /dev/null +++ b/ext/zlib/README @@ -0,0 +1,125 @@ +ZLIB DATA COMPRESSION LIBRARY + +zlib 1.2.3 is a general purpose data compression library. All the code is +thread safe. The data format used by the zlib library is described by RFCs +(Request for Comments) 1950 to 1952 in the files +http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) +and rfc1952.txt (gzip format). These documents are also available in other +formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html + +All functions of the compression library are documented in the file zlib.h +(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example +of the library is given in the file example.c which also tests that the library +is working correctly. Another example is given in the file minigzip.c. The +compression library itself is composed of all source files except example.c and +minigzip.c. + +To compile all files and run the test program, follow the instructions given at +the top of Makefile. In short "make test; make install" should work for most +machines. For Unix: "./configure; make test; make install". For MSDOS, use one +of the special makefiles such as Makefile.msc. For VMS, use make_vms.com. + +Questions about zlib should be sent to , or to Gilles Vollant + for the Windows DLL version. The zlib home page is +http://www.zlib.org or http://www.gzip.org/zlib/ Before reporting a problem, +please check this site to verify that you have the latest version of zlib; +otherwise get the latest version and check whether the problem still exists or +not. + +PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html before asking +for help. + +Mark Nelson wrote an article about zlib for the Jan. 1997 +issue of Dr. Dobb's Journal; a copy of the article is available in +http://dogma.net/markn/articles/zlibtool/zlibtool.htm + +The changes made in version 1.2.3 are documented in the file ChangeLog. + +Unsupported third party contributions are provided in directory "contrib". + +A Java implementation of zlib is available in the Java Development Kit +http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/package-summary.html +See the zlib home page http://www.zlib.org for details. + +A Perl interface to zlib written by Paul Marquess is in the +CPAN (Comprehensive Perl Archive Network) sites +http://www.cpan.org/modules/by-module/Compress/ + +A Python interface to zlib written by A.M. Kuchling is +available in Python 1.5 and later versions, see +http://www.python.org/doc/lib/module-zlib.html + +A zlib binding for TCL written by Andreas Kupries is +availlable at http://www.oche.de/~akupries/soft/trf/trf_zip.html + +An experimental package to read and write files in .zip format, written on top +of zlib by Gilles Vollant , is available in the +contrib/minizip directory of zlib. + + +Notes for some targets: + +- For Windows DLL versions, please see win32/DLL_FAQ.txt + +- For 64-bit Irix, deflate.c must be compiled without any optimization. With + -O, one libpng test fails. The test works in 32 bit mode (with the -n32 + compiler flag). The compiler bug has been reported to SGI. + +- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works + when compiled with cc. + +- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is + necessary to get gzprintf working correctly. This is done by configure. + +- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with + other compilers. Use "make test" to check your compiler. + +- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers. + +- For PalmOs, see http://palmzlib.sourceforge.net/ + +- When building a shared, i.e. dynamic library on Mac OS X, the library must be + installed before testing (do "make install" before "make test"), since the + library location is specified in the library. + + +Acknowledgments: + + The deflate format used by zlib was defined by Phil Katz. The deflate + and zlib specifications were written by L. Peter Deutsch. Thanks to all the + people who reported problems and suggested various improvements in zlib; + they are too numerous to cite here. + +Copyright notice: + + (C) 1995-2004 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +If you use the zlib library in a product, we would appreciate *not* +receiving lengthy legal documents to sign. The sources are provided +for free but without warranty of any kind. The library has been +entirely written by Jean-loup Gailly and Mark Adler; it does not +include third-party code. + +If you redistribute modified sources, we would appreciate that you include +in the file ChangeLog history information documenting your changes. Please +read the FAQ for more information on the distribution of modified source +versions. diff --git a/ext/zlib/adler32.c b/ext/zlib/adler32.c new file mode 100644 index 00000000..f201d670 --- /dev/null +++ b/ext/zlib/adler32.c @@ -0,0 +1,149 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 > BASE) sum1 -= BASE; + if (sum1 > BASE) sum1 -= BASE; + if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 > BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} diff --git a/ext/zlib/compress.c b/ext/zlib/compress.c new file mode 100644 index 00000000..d37e84f5 --- /dev/null +++ b/ext/zlib/compress.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/ext/zlib/crc32.c b/ext/zlib/crc32.c new file mode 100644 index 00000000..32814c20 --- /dev/null +++ b/ext/zlib/crc32.c @@ -0,0 +1,423 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case */ + if (len2 == 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} diff --git a/ext/zlib/crc32.h b/ext/zlib/crc32.h new file mode 100644 index 00000000..5de49bc9 --- /dev/null +++ b/ext/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/ext/zlib/deflate.c b/ext/zlib/deflate.c new file mode 100644 index 00000000..529f716b --- /dev/null +++ b/ext/zlib/deflate.c @@ -0,0 +1,1736 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + /* %%% avoid this when Z_RLE */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt run; /* length of run */ + uInt max; /* maximum length of run */ + uInt prev; /* byte at distance one to match */ + Bytef *scan; /* scan for end of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + run = 0; + if (s->strstart > 0) { /* if there is a previous byte, that is */ + max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + scan = s->window + s->strstart - 1; + prev = *scan++; + do { + if (*scan++ != prev) + break; + } while (++run < max); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (run >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, run); + _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); + s->lookahead -= run; + s->strstart += run; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif diff --git a/ext/zlib/deflate.h b/ext/zlib/deflate.h new file mode 100644 index 00000000..222c53e0 --- /dev/null +++ b/ext/zlib/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/ext/zlib/example.c b/ext/zlib/example.c new file mode 100644 index 00000000..455a1d06 --- /dev/null +++ b/ext/zlib/example.c @@ -0,0 +1,565 @@ +/* example.c -- usage example of the zlib compression library + * Copyright (C) 1995-2004 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include +#include "zlib.h" + +#ifdef STDC +# include +# include +#endif + +#if defined(VMS) || defined(RISCOS) +# define TESTFILE "foo-gz" +#else +# define TESTFILE "foo.gz" +#endif + +#define CHECK_ERR(err, msg) { \ + if (err != Z_OK) { \ + fprintf(stderr, "%s error: %d\n", msg, err); \ + exit(1); \ + } \ +} + +const char hello[] = "hello, hello!"; +/* "hello world" would be more standard, but the repeated "hello" + * stresses the compression code better, sorry... + */ + +const char dictionary[] = "hello"; +uLong dictId; /* Adler32 value of the dictionary */ + +void test_compress OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_gzio OF((const char *fname, + Byte *uncompr, uLong uncomprLen)); +void test_deflate OF((Byte *compr, uLong comprLen)); +void test_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_large_deflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_large_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_flush OF((Byte *compr, uLong *comprLen)); +void test_sync OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_dict_deflate OF((Byte *compr, uLong comprLen)); +void test_dict_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +int main OF((int argc, char *argv[])); + +/* =========================================================================== + * Test compress() and uncompress() + */ +void test_compress(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + uLong len = (uLong)strlen(hello)+1; + + err = compress(compr, &comprLen, (const Bytef*)hello, len); + CHECK_ERR(err, "compress"); + + strcpy((char*)uncompr, "garbage"); + + err = uncompress(uncompr, &uncomprLen, compr, comprLen); + CHECK_ERR(err, "uncompress"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad uncompress\n"); + exit(1); + } else { + printf("uncompress(): %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Test read/write of .gz files + */ +void test_gzio(fname, uncompr, uncomprLen) + const char *fname; /* compressed file name */ + Byte *uncompr; + uLong uncomprLen; +{ +#ifdef NO_GZCOMPRESS + fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n"); +#else + int err; + int len = (int)strlen(hello)+1; + gzFile file; + z_off_t pos; + + file = gzopen(fname, "wb"); + if (file == NULL) { + fprintf(stderr, "gzopen error\n"); + exit(1); + } + gzputc(file, 'h'); + if (gzputs(file, "ello") != 4) { + fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err)); + exit(1); + } + if (gzprintf(file, ", %s!", "hello") != 8) { + fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err)); + exit(1); + } + gzseek(file, 1L, SEEK_CUR); /* add one zero byte */ + gzclose(file); + + file = gzopen(fname, "rb"); + if (file == NULL) { + fprintf(stderr, "gzopen error\n"); + exit(1); + } + strcpy((char*)uncompr, "garbage"); + + if (gzread(file, uncompr, (unsigned)uncomprLen) != len) { + fprintf(stderr, "gzread err: %s\n", gzerror(file, &err)); + exit(1); + } + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad gzread: %s\n", (char*)uncompr); + exit(1); + } else { + printf("gzread(): %s\n", (char*)uncompr); + } + + pos = gzseek(file, -8L, SEEK_CUR); + if (pos != 6 || gztell(file) != pos) { + fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n", + (long)pos, (long)gztell(file)); + exit(1); + } + + if (gzgetc(file) != ' ') { + fprintf(stderr, "gzgetc error\n"); + exit(1); + } + + if (gzungetc(' ', file) != ' ') { + fprintf(stderr, "gzungetc error\n"); + exit(1); + } + + gzgets(file, (char*)uncompr, (int)uncomprLen); + if (strlen((char*)uncompr) != 7) { /* " hello!" */ + fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err)); + exit(1); + } + if (strcmp((char*)uncompr, hello + 6)) { + fprintf(stderr, "bad gzgets after gzseek\n"); + exit(1); + } else { + printf("gzgets() after gzseek: %s\n", (char*)uncompr); + } + + gzclose(file); +#endif +} + +/* =========================================================================== + * Test deflate() with small buffers + */ +void test_deflate(compr, comprLen) + Byte *compr; + uLong comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + uLong len = (uLong)strlen(hello)+1; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_in = (Bytef*)hello; + c_stream.next_out = compr; + + while (c_stream.total_in != len && c_stream.total_out < comprLen) { + c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */ + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + } + /* Finish the stream, still forcing small buffers: */ + for (;;) { + c_stream.avail_out = 1; + err = deflate(&c_stream, Z_FINISH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "deflate"); + } + + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with small buffers + */ +void test_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = 0; + d_stream.next_out = uncompr; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) { + d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */ + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "inflate"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad inflate\n"); + exit(1); + } else { + printf("inflate(): %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Test deflate() with large buffers and dynamic change of compression level + */ +void test_large_deflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_BEST_SPEED); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_out = compr; + c_stream.avail_out = (uInt)comprLen; + + /* At this point, uncompr is still mostly zeroes, so it should compress + * very well: + */ + c_stream.next_in = uncompr; + c_stream.avail_in = (uInt)uncomprLen; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + if (c_stream.avail_in != 0) { + fprintf(stderr, "deflate not greedy\n"); + exit(1); + } + + /* Feed in already compressed data and switch to no compression: */ + deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); + c_stream.next_in = compr; + c_stream.avail_in = (uInt)comprLen/2; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + + /* Switch back to compressing mode: */ + deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED); + c_stream.next_in = uncompr; + c_stream.avail_in = (uInt)uncomprLen; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + fprintf(stderr, "deflate should report Z_STREAM_END\n"); + exit(1); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with large buffers + */ +void test_large_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = (uInt)comprLen; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + for (;;) { + d_stream.next_out = uncompr; /* discard the output */ + d_stream.avail_out = (uInt)uncomprLen; + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "large inflate"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (d_stream.total_out != 2*uncomprLen + comprLen/2) { + fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out); + exit(1); + } else { + printf("large_inflate(): OK\n"); + } +} + +/* =========================================================================== + * Test deflate() with full flush + */ +void test_flush(compr, comprLen) + Byte *compr; + uLong *comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + uInt len = (uInt)strlen(hello)+1; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_in = (Bytef*)hello; + c_stream.next_out = compr; + c_stream.avail_in = 3; + c_stream.avail_out = (uInt)*comprLen; + err = deflate(&c_stream, Z_FULL_FLUSH); + CHECK_ERR(err, "deflate"); + + compr[3]++; /* force an error in first compressed block */ + c_stream.avail_in = len - 3; + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + CHECK_ERR(err, "deflate"); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); + + *comprLen = c_stream.total_out; +} + +/* =========================================================================== + * Test inflateSync() + */ +void test_sync(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = 2; /* just read the zlib header */ + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + d_stream.next_out = uncompr; + d_stream.avail_out = (uInt)uncomprLen; + + inflate(&d_stream, Z_NO_FLUSH); + CHECK_ERR(err, "inflate"); + + d_stream.avail_in = (uInt)comprLen-2; /* read all compressed data */ + err = inflateSync(&d_stream); /* but skip the damaged part */ + CHECK_ERR(err, "inflateSync"); + + err = inflate(&d_stream, Z_FINISH); + if (err != Z_DATA_ERROR) { + fprintf(stderr, "inflate should report DATA_ERROR\n"); + /* Because of incorrect adler32 */ + exit(1); + } + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + printf("after inflateSync(): hel%s\n", (char *)uncompr); +} + +/* =========================================================================== + * Test deflate() with preset dictionary + */ +void test_dict_deflate(compr, comprLen) + Byte *compr; + uLong comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_BEST_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + err = deflateSetDictionary(&c_stream, + (const Bytef*)dictionary, sizeof(dictionary)); + CHECK_ERR(err, "deflateSetDictionary"); + + dictId = c_stream.adler; + c_stream.next_out = compr; + c_stream.avail_out = (uInt)comprLen; + + c_stream.next_in = (Bytef*)hello; + c_stream.avail_in = (uInt)strlen(hello)+1; + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + fprintf(stderr, "deflate should report Z_STREAM_END\n"); + exit(1); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with a preset dictionary + */ +void test_dict_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = (uInt)comprLen; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + d_stream.next_out = uncompr; + d_stream.avail_out = (uInt)uncomprLen; + + for (;;) { + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + if (err == Z_NEED_DICT) { + if (d_stream.adler != dictId) { + fprintf(stderr, "unexpected dictionary"); + exit(1); + } + err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary, + sizeof(dictionary)); + } + CHECK_ERR(err, "inflate with dict"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad inflate with dict\n"); + exit(1); + } else { + printf("inflate with dictionary: %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Usage: example [output.gz [input.gz]] + */ + +int main(argc, argv) + int argc; + char *argv[]; +{ + Byte *compr, *uncompr; + uLong comprLen = 10000*sizeof(int); /* don't overflow on MSDOS */ + uLong uncomprLen = comprLen; + static const char* myVersion = ZLIB_VERSION; + + if (zlibVersion()[0] != myVersion[0]) { + fprintf(stderr, "incompatible zlib version\n"); + exit(1); + + } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) { + fprintf(stderr, "warning: different zlib version\n"); + } + + printf("zlib version %s = 0x%04x, compile flags = 0x%lx\n", + ZLIB_VERSION, ZLIB_VERNUM, zlibCompileFlags()); + + compr = (Byte*)calloc((uInt)comprLen, 1); + uncompr = (Byte*)calloc((uInt)uncomprLen, 1); + /* compr and uncompr are cleared to avoid reading uninitialized + * data and to ensure that uncompr compresses well. + */ + if (compr == Z_NULL || uncompr == Z_NULL) { + printf("out of memory\n"); + exit(1); + } + test_compress(compr, comprLen, uncompr, uncomprLen); + + test_gzio((argc > 1 ? argv[1] : TESTFILE), + uncompr, uncomprLen); + + test_deflate(compr, comprLen); + test_inflate(compr, comprLen, uncompr, uncomprLen); + + test_large_deflate(compr, comprLen, uncompr, uncomprLen); + test_large_inflate(compr, comprLen, uncompr, uncomprLen); + + test_flush(compr, &comprLen); + test_sync(compr, comprLen, uncompr, uncomprLen); + comprLen = uncomprLen; + + test_dict_deflate(compr, comprLen); + test_dict_inflate(compr, comprLen, uncompr, uncomprLen); + + free(compr); + free(uncompr); + + return 0; +} diff --git a/ext/zlib/gzio.c b/ext/zlib/gzio.c new file mode 100644 index 00000000..5e20a4aa --- /dev/null +++ b/ext/zlib/gzio.c @@ -0,0 +1,1026 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + +/* @(#) $Id$ */ + +#include + +#include "zutil.h" + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else if (*p == 'R') { + strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[46]; /* allow for up to 128-bit integers */ + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + start++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= + (uInt)fread(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + if (len == s->stream.avail_out && + (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)) + return -1; + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + len = vsnprintf(buf, sizeof(buf), format, va); + va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Returns 1 if reading and doing so transparently, otherwise zero. +*/ +int ZEXPORT gzdirect (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return 0; + return s->transparent; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + if (do_flush (file, Z_FINISH) != Z_OK) + return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +#ifdef STDC +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +/* =========================================================================== + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void ZEXPORT gzclearerr (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + clearerr(s->file); +} diff --git a/ext/zlib/infback.c b/ext/zlib/infback.c new file mode 100644 index 00000000..1e03e1ba --- /dev/null +++ b/ext/zlib/infback.c @@ -0,0 +1,623 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->write = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + + /* process literal */ + if (this.op == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/ext/zlib/inffast.c b/ext/zlib/inffast.c new file mode 100644 index 00000000..fa31cad9 --- /dev/null +++ b/ext/zlib/inffast.c @@ -0,0 +1,318 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/ext/zlib/inffast.h b/ext/zlib/inffast.h new file mode 100644 index 00000000..614fa787 --- /dev/null +++ b/ext/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/ext/zlib/inffixed.h b/ext/zlib/inffixed.h new file mode 100644 index 00000000..423d5c5b --- /dev/null +++ b/ext/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/ext/zlib/inflate.c b/ext/zlib/inflate.c new file mode 100644 index 00000000..33ea9029 --- /dev/null +++ b/ext/zlib/inflate.c @@ -0,0 +1,1368 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->write = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + state->mode = LIT; + break; + } + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} diff --git a/ext/zlib/inflate.h b/ext/zlib/inflate.h new file mode 100644 index 00000000..fbbc8714 --- /dev/null +++ b/ext/zlib/inflate.h @@ -0,0 +1,115 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/ext/zlib/inftrees.c b/ext/zlib/inftrees.c new file mode 100644 index 00000000..38ded81c --- /dev/null +++ b/ext/zlib/inftrees.c @@ -0,0 +1,329 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/ext/zlib/inftrees.h b/ext/zlib/inftrees.h new file mode 100644 index 00000000..dc0fd567 --- /dev/null +++ b/ext/zlib/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/ext/zlib/minigzip.c b/ext/zlib/minigzip.c new file mode 100644 index 00000000..41996dbc --- /dev/null +++ b/ext/zlib/minigzip.c @@ -0,0 +1,322 @@ +/* minigzip.c -- simulate gzip using the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * minigzip is a minimal implementation of the gzip utility. This is + * only an example of using zlib and isn't meant to replace the + * full-featured gzip. No attempt is made to deal with file systems + * limiting names to 14 or 8+3 characters, etc... Error checking is + * very limited. So use minigzip only for testing; use gzip for the + * real thing. On MSDOS, use only on file names without extension + * or in pipe mode. + */ + +/* @(#) $Id$ */ + +#include +#include "zlib.h" + +#ifdef STDC +# include +# include +#endif + +#ifdef USE_MMAP +# include +# include +# include +#endif + +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) +# include +# include +# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif + +#ifdef VMS +# define unlink delete +# define GZ_SUFFIX "-gz" +#endif +#ifdef RISCOS +# define unlink remove +# define GZ_SUFFIX "-gz" +# define fileno(file) file->__file +#endif +#if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fileno */ +#endif + +#ifndef WIN32 /* unlink already in stdio.h for WIN32 */ + extern int unlink OF((const char *)); +#endif + +#ifndef GZ_SUFFIX +# define GZ_SUFFIX ".gz" +#endif +#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1) + +#define BUFLEN 16384 +#define MAX_NAME_LEN 1024 + +#ifdef MAXSEG_64K +# define local static + /* Needed for systems with limitation on stack size. */ +#else +# define local +#endif + +char *prog; + +void error OF((const char *msg)); +void gz_compress OF((FILE *in, gzFile out)); +#ifdef USE_MMAP +int gz_compress_mmap OF((FILE *in, gzFile out)); +#endif +void gz_uncompress OF((gzFile in, FILE *out)); +void file_compress OF((char *file, char *mode)); +void file_uncompress OF((char *file)); +int main OF((int argc, char *argv[])); + +/* =========================================================================== + * Display error message and exit + */ +void error(msg) + const char *msg; +{ + fprintf(stderr, "%s: %s\n", prog, msg); + exit(1); +} + +/* =========================================================================== + * Compress input to output then close both files. + */ + +void gz_compress(in, out) + FILE *in; + gzFile out; +{ + local char buf[BUFLEN]; + int len; + int err; + +#ifdef USE_MMAP + /* Try first compressing with mmap. If mmap fails (minigzip used in a + * pipe), use the normal fread loop. + */ + if (gz_compress_mmap(in, out) == Z_OK) return; +#endif + for (;;) { + len = (int)fread(buf, 1, sizeof(buf), in); + if (ferror(in)) { + perror("fread"); + exit(1); + } + if (len == 0) break; + + if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err)); + } + fclose(in); + if (gzclose(out) != Z_OK) error("failed gzclose"); +} + +#ifdef USE_MMAP /* MMAP version, Miguel Albrecht */ + +/* Try compressing the input file at once using mmap. Return Z_OK if + * if success, Z_ERRNO otherwise. + */ +int gz_compress_mmap(in, out) + FILE *in; + gzFile out; +{ + int len; + int err; + int ifd = fileno(in); + caddr_t buf; /* mmap'ed buffer for the entire input file */ + off_t buf_len; /* length of the input file */ + struct stat sb; + + /* Determine the size of the file, needed for mmap: */ + if (fstat(ifd, &sb) < 0) return Z_ERRNO; + buf_len = sb.st_size; + if (buf_len <= 0) return Z_ERRNO; + + /* Now do the actual mmap: */ + buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0); + if (buf == (caddr_t)(-1)) return Z_ERRNO; + + /* Compress the whole file at once: */ + len = gzwrite(out, (char *)buf, (unsigned)buf_len); + + if (len != (int)buf_len) error(gzerror(out, &err)); + + munmap(buf, buf_len); + fclose(in); + if (gzclose(out) != Z_OK) error("failed gzclose"); + return Z_OK; +} +#endif /* USE_MMAP */ + +/* =========================================================================== + * Uncompress input to output then close both files. + */ +void gz_uncompress(in, out) + gzFile in; + FILE *out; +{ + local char buf[BUFLEN]; + int len; + int err; + + for (;;) { + len = gzread(in, buf, sizeof(buf)); + if (len < 0) error (gzerror(in, &err)); + if (len == 0) break; + + if ((int)fwrite(buf, 1, (unsigned)len, out) != len) { + error("failed fwrite"); + } + } + if (fclose(out)) error("failed fclose"); + + if (gzclose(in) != Z_OK) error("failed gzclose"); +} + + +/* =========================================================================== + * Compress the given file: create a corresponding .gz file and remove the + * original. + */ +void file_compress(file, mode) + char *file; + char *mode; +{ + local char outfile[MAX_NAME_LEN]; + FILE *in; + gzFile out; + + strcpy(outfile, file); + strcat(outfile, GZ_SUFFIX); + + in = fopen(file, "rb"); + if (in == NULL) { + perror(file); + exit(1); + } + out = gzopen(outfile, mode); + if (out == NULL) { + fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile); + exit(1); + } + gz_compress(in, out); + + unlink(file); +} + + +/* =========================================================================== + * Uncompress the given file and remove the original. + */ +void file_uncompress(file) + char *file; +{ + local char buf[MAX_NAME_LEN]; + char *infile, *outfile; + FILE *out; + gzFile in; + uInt len = (uInt)strlen(file); + + strcpy(buf, file); + + if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) { + infile = file; + outfile = buf; + outfile[len-3] = '\0'; + } else { + outfile = file; + infile = buf; + strcat(infile, GZ_SUFFIX); + } + in = gzopen(infile, "rb"); + if (in == NULL) { + fprintf(stderr, "%s: can't gzopen %s\n", prog, infile); + exit(1); + } + out = fopen(outfile, "wb"); + if (out == NULL) { + perror(file); + exit(1); + } + + gz_uncompress(in, out); + + unlink(infile); +} + + +/* =========================================================================== + * Usage: minigzip [-d] [-f] [-h] [-r] [-1 to -9] [files...] + * -d : decompress + * -f : compress with Z_FILTERED + * -h : compress with Z_HUFFMAN_ONLY + * -r : compress with Z_RLE + * -1 to -9 : compression level + */ + +int main(argc, argv) + int argc; + char *argv[]; +{ + int uncompr = 0; + gzFile file; + char outmode[20]; + + strcpy(outmode, "wb6 "); + + prog = argv[0]; + argc--, argv++; + + while (argc > 0) { + if (strcmp(*argv, "-d") == 0) + uncompr = 1; + else if (strcmp(*argv, "-f") == 0) + outmode[3] = 'f'; + else if (strcmp(*argv, "-h") == 0) + outmode[3] = 'h'; + else if (strcmp(*argv, "-r") == 0) + outmode[3] = 'R'; + else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' && + (*argv)[2] == 0) + outmode[2] = (*argv)[1]; + else + break; + argc--, argv++; + } + if (outmode[3] == ' ') + outmode[3] = 0; + if (argc == 0) { + SET_BINARY_MODE(stdin); + SET_BINARY_MODE(stdout); + if (uncompr) { + file = gzdopen(fileno(stdin), "rb"); + if (file == NULL) error("can't gzdopen stdin"); + gz_uncompress(file, stdout); + } else { + file = gzdopen(fileno(stdout), outmode); + if (file == NULL) error("can't gzdopen stdout"); + gz_compress(stdin, file); + } + } else { + do { + if (uncompr) { + file_uncompress(*argv); + } else { + file_compress(*argv, outmode); + } + } while (argv++, --argc); + } + return 0; +} diff --git a/ext/zlib/trees.c b/ext/zlib/trees.c new file mode 100644 index 00000000..7a048028 --- /dev/null +++ b/ext/zlib/trees.c @@ -0,0 +1,1219 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2005 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) + set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type(s) + deflate_state *s; +{ + int n; + + for (n = 0; n < 9; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + if (n == 9) + for (n = 14; n < 32; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/ext/zlib/trees.h b/ext/zlib/trees.h new file mode 100644 index 00000000..1ca868b8 --- /dev/null +++ b/ext/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/ext/zlib/uncompr.c b/ext/zlib/uncompr.c new file mode 100644 index 00000000..ad6db0a6 --- /dev/null +++ b/ext/zlib/uncompr.c @@ -0,0 +1,61 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/ext/zlib/win32/zlib.def b/ext/zlib/win32/zlib.def new file mode 100644 index 00000000..03bcf1c5 --- /dev/null +++ b/ext/zlib/win32/zlib.def @@ -0,0 +1,60 @@ +LIBRARY +; zlib data compression library + +EXPORTS +; basic functions + zlibVersion + deflate + deflateEnd + inflate + inflateEnd +; advanced functions + deflateSetDictionary + deflateCopy + deflateReset + deflateParams + deflateBound + deflatePrime + inflateSetDictionary + inflateSync + inflateCopy + inflateReset + inflateBack + inflateBackEnd + zlibCompileFlags +; utility functions + compress + compress2 + compressBound + uncompress + gzopen + gzdopen + gzsetparams + gzread + gzwrite + gzprintf + gzputs + gzgets + gzputc + gzgetc + gzungetc + gzflush + gzseek + gzrewind + gztell + gzeof + gzclose + gzerror + gzclearerr +; checksum functions + adler32 + crc32 +; various hacks, don't look :) + deflateInit_ + deflateInit2_ + inflateInit_ + inflateInit2_ + inflateBackInit_ + inflateSyncPoint + get_crc_table + zError diff --git a/ext/zlib/win32/zlib1.rc b/ext/zlib/win32/zlib1.rc new file mode 100644 index 00000000..023a8433 --- /dev/null +++ b/ext/zlib/win32/zlib1.rc @@ -0,0 +1,39 @@ +#include + +#ifdef GCC_WINDRES +VS_VERSION_INFO VERSIONINFO +#else +VS_VERSION_INFO VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE +#endif + FILEVERSION 1,2,2,0 + PRODUCTVERSION 1,2,2,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS 1 +#else + FILEFLAGS 0 +#endif + FILEOS VOS_DOS_WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0 // not used +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + //language ID = U.S. English, char set = Windows, Multilingual + BEGIN + VALUE "FileDescription", "zlib data compression library\0" + VALUE "FileVersion", "1.2.3\0" + VALUE "InternalName", "zlib1.dll\0" + VALUE "LegalCopyright", "(C) 1995-2004 Jean-loup Gailly & Mark Adler\0" + VALUE "OriginalFilename", "zlib1.dll\0" + VALUE "ProductName", "zlib\0" + VALUE "ProductVersion", "1.2.3\0" + VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1252 + END +END diff --git a/ext/zlib/zconf.h b/ext/zlib/zconf.h new file mode 100644 index 00000000..e3b0c962 --- /dev/null +++ b/ext/zlib/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/ext/zlib/zconf.in.h b/ext/zlib/zconf.in.h new file mode 100644 index 00000000..e3b0c962 --- /dev/null +++ b/ext/zlib/zconf.in.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/ext/zlib/zlib.h b/ext/zlib/zlib.h new file mode 100644 index 00000000..62d0e467 --- /dev/null +++ b/ext/zlib/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/ext/zlib/zutil.c b/ext/zlib/zutil.c new file mode 100644 index 00000000..0f4bd787 --- /dev/null +++ b/ext/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/ext/zlib/zutil.h b/ext/zlib/zutil.h new file mode 100644 index 00000000..0ba6e020 --- /dev/null +++ b/ext/zlib/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include +# endif +# include +# include +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/gamescreen.cpp b/gamescreen.cpp index e24aba5d..fb162148 100644 --- a/gamescreen.cpp +++ b/gamescreen.cpp @@ -1118,9 +1118,16 @@ void InitHelicopterEntranceByMercs( void ) if( (gGameExternalOptions.iGameStartingTime + gGameExternalOptions. iFirstArrivalDelay) < 111600 || (gGameExternalOptions.iGameStartingTime + gGameExternalOptions.iFirstArrivalDelay >= 162000)) { + // Default: Night gubEnvLightValue = 12; - LightSetBaseLevel(gubEnvLightValue); - } + } + else + { + // Default: Day + gubEnvLightValue = 3; + } + + LightSetBaseLevel(gubEnvLightValue); AirRaidDef.sSectorX = 9; AirRaidDef.sSectorY = 1; diff --git a/ja2_2005Express.sln b/ja2_2005Express.sln index 315f4439..b2e4893e 100644 --- a/ja2_2005Express.sln +++ b/ja2_2005Express.sln @@ -1,18 +1,22 @@  Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual C++ Express 2005 +# Visual Studio 2005 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ja2_2005Express", "ja2_2005Express.vcproj", "{F44669E7-74AC-444B-B75F-F16F4B9F0265}" ProjectSection(ProjectDependencies) = postProject - {5234CD2E-97F1-42FF-828D-CE0A1B46805E} = {5234CD2E-97F1-42FF-828D-CE0A1B46805E} - {F962CFA1-2215-4124-938F-253973A27591} = {F962CFA1-2215-4124-938F-253973A27591} - {60D793F2-90B4-43C9-83B5-221EE11B37E6} = {60D793F2-90B4-43C9-83B5-221EE11B37E6} - {FC1938E8-9253-49A8-AA99-4639BBB46C1B} = {FC1938E8-9253-49A8-AA99-4639BBB46C1B} - {AB8837DB-0435-40C7-916A-0AACF5CFF1C4} = {AB8837DB-0435-40C7-916A-0AACF5CFF1C4} - {A83DA7DA-2C31-45D8-9A69-B4993A885E76} = {A83DA7DA-2C31-45D8-9A69-B4993A885E76} - {27A49DAF-3F5A-4FF2-B943-9559F0B63032} = {27A49DAF-3F5A-4FF2-B943-9559F0B63032} - {D4BDA6AD-9B61-4953-892D-DA3F8CC7E096} = {D4BDA6AD-9B61-4953-892D-DA3F8CC7E096} - {6283CE93-2960-4596-8E74-7A6FBA8716FF} = {6283CE93-2960-4596-8E74-7A6FBA8716FF} + {A67922A0-0A3F-497F-A724-19B513EFC078} = {A67922A0-0A3F-497F-A724-19B513EFC078} + {C63466D6-49B5-4C54-AA0D-864F6171FD9B} = {C63466D6-49B5-4C54-AA0D-864F6171FD9B} + {4B544F1A-6188-4255-8877-69FC312D0D2C} = {4B544F1A-6188-4255-8877-69FC312D0D2C} + {9AD14886-DB5C-4EFF-AE17-35F9BD684692} = {9AD14886-DB5C-4EFF-AE17-35F9BD684692} {262A5F80-0B99-4E8E-A6C7-74CB1FD0A67C} = {262A5F80-0B99-4E8E-A6C7-74CB1FD0A67C} + {6283CE93-2960-4596-8E74-7A6FBA8716FF} = {6283CE93-2960-4596-8E74-7A6FBA8716FF} + {D4BDA6AD-9B61-4953-892D-DA3F8CC7E096} = {D4BDA6AD-9B61-4953-892D-DA3F8CC7E096} + {27A49DAF-3F5A-4FF2-B943-9559F0B63032} = {27A49DAF-3F5A-4FF2-B943-9559F0B63032} + {A83DA7DA-2C31-45D8-9A69-B4993A885E76} = {A83DA7DA-2C31-45D8-9A69-B4993A885E76} + {AB8837DB-0435-40C7-916A-0AACF5CFF1C4} = {AB8837DB-0435-40C7-916A-0AACF5CFF1C4} + {FC1938E8-9253-49A8-AA99-4639BBB46C1B} = {FC1938E8-9253-49A8-AA99-4639BBB46C1B} + {60D793F2-90B4-43C9-83B5-221EE11B37E6} = {60D793F2-90B4-43C9-83B5-221EE11B37E6} + {F962CFA1-2215-4124-938F-253973A27591} = {F962CFA1-2215-4124-938F-253973A27591} + {5234CD2E-97F1-42FF-828D-CE0A1B46805E} = {5234CD2E-97F1-42FF-828D-CE0A1B46805E} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Editor_2005Express", "Editor\Editor_2005Express.vcproj", "{60D793F2-90B4-43C9-83B5-221EE11B37E6}" @@ -35,6 +39,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Utils_2005Express", "Utils\ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Console_2005Express", "Console\Console_2005Express.vcproj", "{F962CFA1-2215-4124-938F-253973A27591}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VFS_2005Express", "VFS\VFS_2005Express.vcproj", "{C63466D6-49B5-4C54-AA0D-864F6171FD9B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "7z_2005Express", "ext\7z\Archive\7z\7z_2005Express.vcproj", "{9AD14886-DB5C-4EFF-AE17-35F9BD684692}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpng_2005Express", "ext\libpng\projects\visualc71\libpng_2005Express.vcproj", "{4B544F1A-6188-4255-8877-69FC312D0D2C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib_2005Express", "ext\libpng\projects\visualc71\zlib_2005Express.vcproj", "{A67922A0-0A3F-497F-A724-19B513EFC078}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -131,6 +143,38 @@ Global {F962CFA1-2215-4124-938F-253973A27591}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 {F962CFA1-2215-4124-938F-253973A27591}.Release|Win32.ActiveCfg = Release|Win32 {F962CFA1-2215-4124-938F-253973A27591}.Release|Win32.Build.0 = Release|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.Debug|Win32.ActiveCfg = Debug|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.Debug|Win32.Build.0 = Debug|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.MapEditor|Win32.ActiveCfg = MapEditor|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.MapEditor|Win32.Build.0 = MapEditor|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.MapEditorD|Win32.ActiveCfg = MapEditorD|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.Release|Win32.ActiveCfg = Release|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.Release|Win32.Build.0 = Release|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.Debug|Win32.ActiveCfg = Debug|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.Debug|Win32.Build.0 = Debug|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.MapEditor|Win32.ActiveCfg = MapEditor|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.MapEditor|Win32.Build.0 = MapEditor|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.MapEditorD|Win32.ActiveCfg = MapEditorD|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.Release|Win32.ActiveCfg = Release|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.Release|Win32.Build.0 = Release|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.Debug|Win32.ActiveCfg = Debug|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.Debug|Win32.Build.0 = Debug|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.MapEditor|Win32.ActiveCfg = MapEditor|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.MapEditor|Win32.Build.0 = MapEditor|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.MapEditorD|Win32.ActiveCfg = MapEditorD|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.Release|Win32.ActiveCfg = Release|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.Release|Win32.Build.0 = Release|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.Debug|Win32.ActiveCfg = Debug|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.Debug|Win32.Build.0 = Debug|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.MapEditor|Win32.ActiveCfg = MapEditor|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.MapEditor|Win32.Build.0 = MapEditor|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.MapEditorD|Win32.ActiveCfg = MapEditorD|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.Release|Win32.ActiveCfg = Release|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ja2_2005Express.vcproj b/ja2_2005Express.vcproj index de5759bb..0a5b84ff 100644 --- a/ja2_2005Express.vcproj +++ b/ja2_2005Express.vcproj @@ -17,7 +17,7 @@ + + @@ -570,6 +574,10 @@ RelativePath=".\MPChatScreen.h" > + + diff --git a/ja2_VS2008.sln b/ja2_VS2008.sln index ae7fdc06..7d68738a 100644 --- a/ja2_VS2008.sln +++ b/ja2_VS2008.sln @@ -13,6 +13,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ja2", "ja2_VS2008.vcproj", {DF8C5F50-6FB1-4AFE-8EF4-6B7F360D9C28} = {DF8C5F50-6FB1-4AFE-8EF4-6B7F360D9C28} {629F0A55-6A19-4107-8B5C-5B08B2B3949C} = {629F0A55-6A19-4107-8B5C-5B08B2B3949C} {DB40B90D-13F9-4FEC-8F1D-0506CC496586} = {DB40B90D-13F9-4FEC-8F1D-0506CC496586} + {4B544F1A-6188-4255-8877-69FC312D0D2C} = {4B544F1A-6188-4255-8877-69FC312D0D2C} + {9AD14886-DB5C-4EFF-AE17-35F9BD684692} = {9AD14886-DB5C-4EFF-AE17-35F9BD684692} + {A67922A0-0A3F-497F-A724-19B513EFC078} = {A67922A0-0A3F-497F-A724-19B513EFC078} + {C63466D6-49B5-4C54-AA0D-864F6171FD9B} = {C63466D6-49B5-4C54-AA0D-864F6171FD9B} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Console", "Console\Console_VS2008.vcproj", "{0BE7070B-C8C5-409A-82DD-0701D54A1C6C}" @@ -35,6 +39,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TileEngine", "TileEngine\Ti EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Utils", "Utils\Utils_VS2008.vcproj", "{DB40B90D-13F9-4FEC-8F1D-0506CC496586}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VFS", "VFS\VFS_VS2008.vcproj", "{C63466D6-49B5-4C54-AA0D-864F6171FD9B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpng", "ext\libpng\projects\visualc71\libpng.vcproj", "{4B544F1A-6188-4255-8877-69FC312D0D2C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "ext\libpng\projects\visualc71\zlib.vcproj", "{A67922A0-0A3F-497F-A724-19B513EFC078}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "7z", "ext\7z\Archive\7z\7z.vcproj", "{9AD14886-DB5C-4EFF-AE17-35F9BD684692}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -51,6 +63,14 @@ Global {B112F6BA-AF32-489C-B405-916C0F182D10}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 {B112F6BA-AF32-489C-B405-916C0F182D10}.Release|Win32.ActiveCfg = Release|Win32 {B112F6BA-AF32-489C-B405-916C0F182D10}.Release|Win32.Build.0 = Release|Win32 + {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.Debug|Win32.ActiveCfg = Debug|Win32 + {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.Debug|Win32.Build.0 = Debug|Win32 + {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.MapEditor|Win32.ActiveCfg = MapEditor|Win32 + {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.MapEditor|Win32.Build.0 = MapEditor|Win32 + {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.MapEditorD|Win32.ActiveCfg = MapEditorD|Win32 + {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 + {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.Release|Win32.ActiveCfg = Release|Win32 + {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.Release|Win32.Build.0 = Release|Win32 {99341DB1-FD6E-4E87-919B-25C813F08D18}.Debug|Win32.ActiveCfg = Debug|Win32 {99341DB1-FD6E-4E87-919B-25C813F08D18}.Debug|Win32.Build.0 = Debug|Win32 {99341DB1-FD6E-4E87-919B-25C813F08D18}.MapEditor|Win32.ActiveCfg = MapEditor|Win32 @@ -75,6 +95,14 @@ Global {60D5BF89-B609-4A30-A38D-9FE417FB12B6}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 {60D5BF89-B609-4A30-A38D-9FE417FB12B6}.Release|Win32.ActiveCfg = Release|Win32 {60D5BF89-B609-4A30-A38D-9FE417FB12B6}.Release|Win32.Build.0 = Release|Win32 + {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.Debug|Win32.ActiveCfg = Debug|Win32 + {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.Debug|Win32.Build.0 = Debug|Win32 + {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.MapEditor|Win32.ActiveCfg = MapEditor|Win32 + {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.MapEditor|Win32.Build.0 = MapEditor|Win32 + {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.MapEditorD|Win32.ActiveCfg = MapEditorD|Win32 + {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 + {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.Release|Win32.ActiveCfg = Release|Win32 + {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.Release|Win32.Build.0 = Release|Win32 {40475E2B-AD37-4A99-85A4-1E507010344C}.Debug|Win32.ActiveCfg = Debug|Win32 {40475E2B-AD37-4A99-85A4-1E507010344C}.Debug|Win32.Build.0 = Debug|Win32 {40475E2B-AD37-4A99-85A4-1E507010344C}.MapEditor|Win32.ActiveCfg = MapEditor|Win32 @@ -115,22 +143,38 @@ Global {DB40B90D-13F9-4FEC-8F1D-0506CC496586}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 {DB40B90D-13F9-4FEC-8F1D-0506CC496586}.Release|Win32.ActiveCfg = Release|Win32 {DB40B90D-13F9-4FEC-8F1D-0506CC496586}.Release|Win32.Build.0 = Release|Win32 - {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.Debug|Win32.ActiveCfg = Debug|Win32 - {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.Debug|Win32.Build.0 = Debug|Win32 - {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.MapEditor|Win32.ActiveCfg = MapEditor|Win32 - {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.MapEditor|Win32.Build.0 = MapEditor|Win32 - {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.MapEditorD|Win32.ActiveCfg = MapEditorD|Win32 - {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 - {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.Release|Win32.ActiveCfg = Release|Win32 - {0BE7070B-C8C5-409A-82DD-0701D54A1C6C}.Release|Win32.Build.0 = Release|Win32 - {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.Debug|Win32.ActiveCfg = Debug|Win32 - {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.Debug|Win32.Build.0 = Debug|Win32 - {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.MapEditor|Win32.ActiveCfg = MapEditor|Win32 - {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.MapEditor|Win32.Build.0 = MapEditor|Win32 - {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.MapEditorD|Win32.ActiveCfg = MapEditorD|Win32 - {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 - {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.Release|Win32.ActiveCfg = Release|Win32 - {96FA90B1-2ABD-42E7-9CAB-14804DFD3240}.Release|Win32.Build.0 = Release|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.Debug|Win32.ActiveCfg = Debug|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.Debug|Win32.Build.0 = Debug|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.MapEditor|Win32.ActiveCfg = MapEditor|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.MapEditor|Win32.Build.0 = MapEditor|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.MapEditorD|Win32.ActiveCfg = MapEditorD|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.Release|Win32.ActiveCfg = Release|Win32 + {C63466D6-49B5-4C54-AA0D-864F6171FD9B}.Release|Win32.Build.0 = Release|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.Debug|Win32.ActiveCfg = Debug|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.Debug|Win32.Build.0 = Debug|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.MapEditor|Win32.ActiveCfg = MapEditor|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.MapEditor|Win32.Build.0 = MapEditor|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.MapEditorD|Win32.ActiveCfg = MapEditorD|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.Release|Win32.ActiveCfg = Release|Win32 + {4B544F1A-6188-4255-8877-69FC312D0D2C}.Release|Win32.Build.0 = Release|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.Debug|Win32.ActiveCfg = Debug|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.Debug|Win32.Build.0 = Debug|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.MapEditor|Win32.ActiveCfg = MapEditor|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.MapEditor|Win32.Build.0 = MapEditor|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.MapEditorD|Win32.ActiveCfg = MapEditorD|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.Release|Win32.ActiveCfg = Release|Win32 + {A67922A0-0A3F-497F-A724-19B513EFC078}.Release|Win32.Build.0 = Release|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.Debug|Win32.ActiveCfg = Debug|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.Debug|Win32.Build.0 = Debug|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.MapEditor|Win32.ActiveCfg = MapEditor|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.MapEditor|Win32.Build.0 = MapEditor|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.MapEditorD|Win32.ActiveCfg = MapEditorD|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.MapEditorD|Win32.Build.0 = MapEditorD|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.Release|Win32.ActiveCfg = Release|Win32 + {9AD14886-DB5C-4EFF-AE17-35F9BD684692}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ja2_VS2008.vcproj b/ja2_VS2008.vcproj index 7a445d88..4e7d24be 100644 --- a/ja2_VS2008.vcproj +++ b/ja2_VS2008.vcproj @@ -18,8 +18,8 @@ + + @@ -617,6 +621,10 @@ RelativePath=".\MPChatScreen.cpp" > + + diff --git a/ja2_VS2008.vsprops b/ja2_VS2008.vsprops index 9f699b8b..7a556fda 100644 --- a/ja2_VS2008.vsprops +++ b/ja2_VS2008.vsprops @@ -6,7 +6,7 @@ > diff --git a/ja2_VS2008Linker.vsprops b/ja2_VS2008Linker.vsprops index 91d71eac..6ca9f6f9 100644 --- a/ja2_VS2008Linker.vsprops +++ b/ja2_VS2008Linker.vsprops @@ -7,6 +7,6 @@ diff --git a/jascreens.cpp b/jascreens.cpp index ff19dde9..3398dbe8 100644 --- a/jascreens.cpp +++ b/jascreens.cpp @@ -61,6 +61,8 @@ #include "IniReader.h" #endif +#include "VFS/vfs.h" + #define _UNICODE // Networking Stuff #ifdef NETWORKED @@ -75,6 +77,7 @@ extern BOOLEAN gfAmINetworked; #define MAX_DEBUG_PAGES 4 +std::list g_ExceptionList; // GLOBAL FOR PAL EDITOR UINT8 CurrentPalette = 0; @@ -277,7 +280,12 @@ UINT32 ErrorScreenHandle(void) //StartFrameBufferRender( ); SetFontDestBuffer(FRAME_BUFFER,0,0,SCREEN_WIDTH,SCREEN_HEIGHT,FALSE ); - + if(!g_ExceptionList.empty()) + { + PrintExceptionList(); + } + else + { // Create string SetFont( LARGEFONT1 ); SetFontBackground( FONT_MCOLOR_BLACK ); @@ -301,7 +309,7 @@ UINT32 ErrorScreenHandle(void) DisplayWrappedString( 50, 270, 560, 2, FONT12ARIAL, FONT_RED, str, FONT_BLACK, TRUE, LEFT_JUSTIFIED ); } #endif - + } if ( !fFirstTime ) { DebugMsg(TOPIC_JA2, DBG_LEVEL_0, String( "Runtime Error: %s ", gubErrorText ) ); @@ -327,6 +335,9 @@ UINT32 ErrorScreenHandle(void) } } + // there is no need to create 100 % cpu load in this stage of the game + Sleep(50); + return( ERROR_SCREEN ); } @@ -428,15 +439,23 @@ UINT32 InitScreenHandle(void) // Handle queued .ini file error messages int y = 40; +#ifdef USE_VFS + CLog logger(L"ERROR_REPORT.iniErrorMessages.txt"); + //logger.SetAppend(); +#endif while (! iniErrorMessages.empty()) { +#ifndef USE_VFS FILE *file_pointer; +#endif static BOOL iniErrorMessage_create_out_file = TRUE; std::string iniErrorMessage = iniErrorMessages.top(); CHAR16 str[256]; if (iniErrorMessage_create_out_file) { +#ifndef USE_VFS fopen_s( &file_pointer, "..\\ERROR_REPORT.iniErrorMessages.txt", "w" ); +#endif y += 25; swprintf( str, L"%S", "ERROR_REPORT.iniErrorMessages.txt has been created. Please review its content." ); DisplayWrappedString( 10, y, 560, 2, FONT12ARIAL, FONT_RED, str, FONT_BLACK, TRUE, LEFT_JUSTIFIED ); @@ -444,13 +463,19 @@ UINT32 InitScreenHandle(void) } else { +#ifndef USE_VFS fopen_s( &file_pointer, "..\\ERROR_REPORT.iniErrorMessages.txt", "a+" ); +#endif } y += 25; swprintf( str, L"%S", iniErrorMessage.c_str() ); +#ifndef USE_VFS fprintf_s (file_pointer , "%S\n" , str ); fclose( file_pointer ); +#else + logger << iniErrorMessage << CLog::endl; +#endif DisplayWrappedString( 10, y, 560, 2, FONT12ARIAL, FONT_RED, str, FONT_BLACK, TRUE, LEFT_JUSTIFIED ); iniErrorMessages.pop(); @@ -1080,3 +1105,53 @@ UINT32 EditScreenShutdown() #endif + +void PrintExceptionList() +{ + UINT8* pDestBuf; + UINT32 uiDestPitchBYTES; + SGPRect ClipRect; + + ClipRect.iLeft = 0; + ClipRect.iRight = SCREEN_WIDTH; + ClipRect.iTop = 0; + ClipRect.iBottom = SCREEN_HEIGHT; + + pDestBuf = LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES ); + //Blt16BPPBufferShadowRect( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect ); + Blt16BPPBufferHatchRect( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect ); + UnLockVideoSurface( FRAME_BUFFER ); + + // Create string + SetFont( LARGEFONT1 ); + SetFontBackground( FONT_MCOLOR_BLACK ); + SetFontForeground( FONT_MCOLOR_LTGRAY ); + mprintf( 50, 200, L"RUNTIME ERROR" ); + + mprintf( 50, 225, L"PRESS TO EXIT" ); + + const int iStartY = 255; + const int iDiffY = 70; + + std::list::iterator it = g_ExceptionList.begin(); + for(int i = 0; it != g_ExceptionList.end(); ++it, ++i) + { + //std::stringstream ss; + //ss << (*it).file << "(l. " << it->line; + SetFont( FONT12ARIAL ); + SetFontForeground( FONT_YELLOW ); + SetFontShadow( 60 ); //60 is near black + mprintf( 50, iStartY+(i*iDiffY) , L"File"); + mprintf( 100, iStartY+(i*iDiffY) , L": %s", it->file.c_wcs().c_str() ); + + mprintf( 50, iStartY+(i*iDiffY)+14, L"Line"); + mprintf( 100, iStartY+(i*iDiffY)+14, L": %i", it->line ); + + mprintf( 50, iStartY+(i*iDiffY)+28, L"Function"); + mprintf( 100, iStartY+(i*iDiffY)+28, L": %s", it->function.c_wcs().c_str() ); + + SetFontForeground( FONT_LTRED ); + DisplayWrappedString( 60, iStartY+(i*iDiffY) + 44, SCREEN_WIDTH - 100, 2, FONT12ARIAL, FONT_RED, const_cast(it->message.c_wcs().c_str()), FONT_BLACK, TRUE, LEFT_JUSTIFIED ); + } +} + diff --git a/jascreens.h b/jascreens.h index 33ccbab3..38e36857 100644 --- a/jascreens.h +++ b/jascreens.h @@ -5,6 +5,8 @@ #include "ScreenIds.h" #include "mousesystem.h" #include "Button System.h" +#include +#include "VFS/utf8string.h" extern UINT32 EditScreenInit(void); extern UINT32 EditScreenHandle(void); @@ -137,6 +139,11 @@ extern UINT32 MPChatScreenInit( void ); extern UINT32 MPChatScreenHandle( void ); extern UINT32 MPChatScreenShutdown( void ); +// OJW - 20090422 +extern UINT32 MPConnectScreenInit( void ); +extern UINT32 MPConnectScreenHandle( void ); +extern UINT32 MPConnectScreenShutdown( void ); + // External functions void DisplayFrameRate( ); @@ -145,6 +152,7 @@ void HandleTitleScreenAnimation(); //External Globals extern CHAR8 gubFilename[ 200 ]; extern UINT32 guiCurrentScreen; +extern UINT32 guiPendingScreen; typedef void (*RENDER_HOOK)( void ); @@ -157,5 +165,17 @@ void DisableFPSOverlay( BOOLEAN fEnable ); void EnterTacticalScreen( ); void LeaveTacticalScreen( UINT32 uiNewScreen ); +////////////////////////////////////////////////////////////////////////// + +struct SExceptionData +{ + utf8string message,function,file; + int line; +}; + +extern std::list g_ExceptionList; + +void PrintExceptionList(); + #endif \ No newline at end of file diff --git a/mainmenuscreen.h b/mainmenuscreen.h index b343b2f2..1da1d813 100644 --- a/mainmenuscreen.h +++ b/mainmenuscreen.h @@ -5,6 +5,6 @@ BOOLEAN InitMainMenu( ); void ClearMainMenu( ); -void InitDependingGameStyleOptions(BOOLEAN isNetworked); +void InitDependingGameStyleOptions(); #endif \ No newline at end of file diff --git a/screenids.h b/screenids.h index 7e7f86a1..357fe53d 100644 --- a/screenids.h +++ b/screenids.h @@ -32,6 +32,7 @@ typedef enum ScreenTypes MP_HOST_SCREEN, MP_SCORE_SCREEN, // OJW - 20081222 MP_CHAT_SCREEN, // OJW - 20090315 + MP_CONNECT_SCREEN, //OJW - 20090422 #ifdef JA2BETAVERSION AIVIEWER_SCREEN,