diff --git a/code/netshare.cpp b/code/netshare.cpp index bccfa8d28..70e4a6a92 100644 --- a/code/netshare.cpp +++ b/code/netshare.cpp @@ -42,6 +42,7 @@ #include #include +#include const COLORREF ColorSystem = RGB(255, 255, 255)|(255<<24); /// 0xFFFFFFFF @@ -1346,6 +1347,62 @@ void Update_Network_Dialog_Preview(HWND win) } +/// +/// Expands a downloaded preview file and makes it the multiplayer preview. +/// +/// Name of the compressed preview file the host sent. +/// bool; Was the preview replaced? A block that does not describe an image fitting +/// the bytes it arrived in is refused and the current preview is left alone. +static bool Load_Random_Map_Preview(char const * filename) +{ + DebugString("Loading the compressed preview image\n"); + CDFileClass file(filename); + + int size = file.Size(); + if (size <= (int)sizeof(int)) { + DebugString("Preview file size %d is too small\n", size); + return(false); + } + + std::vector buffer(size); + file.Read(buffer.data(), size); + + int preview_size = ((int *)buffer.data())[0]; + if (preview_size <= 0 || preview_size > MapPreviewClass::MAX_BLOCK_SIZE) { + DebugString("Preview decompressed size %d is out of range\n", preview_size); + return(false); + } + + DebugString("Decompressing the preview image\n"); + + BufferStraw bstraw(buffer.data() + sizeof(int), size - (int)sizeof(int)); + LZOStraw lzostraw(LZOStraw::DECOMPRESS); + lzostraw.Get_From(&bstraw); + + std::vector preview(preview_size); + int expanded = lzostraw.Get(preview.data(), preview_size); + if (expanded != preview_size) { + DebugString("Preview decompressed size %d doesn't match expected %d bytes\n", expanded, preview_size); + return(false); + } + + DebugString("Creating the new preview surface\n"); + + MapPreviewClass * created = new MapPreviewClass; + if (!created->Create_Preview_Surface(preview.data(), preview_size)) { + DebugString("Preview surface could not be created\n"); + delete created; + return(false); + } + + if (MultiplayerMapPreview) { + delete MultiplayerMapPreview; + } + MultiplayerMapPreview = created; + return(true); +} + + /// /// Receives the random map preview image from the host. /// This is the guest side of the preview handshake. The host is told that this machine is @@ -1375,39 +1432,11 @@ void Receive_Random_Map_Preview(void) Session.GAddress = Session.HostAddress; DebugString("Calling Get_File_From_Host to receive the file download\n"); char preview_name[256]; - bool got_file = Get_File_From_Host(preview_name, false); - if (!got_file) { - DebugString("got_file is false. Download failed\n"); - Ipx.Set_Timing(TIMER_SECOND / 2, -1, 10 * TIMER_SECOND); - return; - } - - DebugString("Loading the compressed preview image\n"); - CDFileClass file(preview_name); - int size = file.Size(); - char * buffer = new char[size]; - file.Read(buffer, size); - int preview_size = ((int *)buffer)[0]; - - DebugString("Decompressing the preview image\n"); - - BufferStraw bstraw(&((int *)buffer)[1], size); - LZOStraw lzostraw(LZOStraw::DECOMPRESS); - lzostraw.Get_From(&bstraw); - char * preview = new char[2 * preview_size]; - lzostraw.Get(preview, preview_size); - - DebugString("Creating the new preview surface\n"); - if (MultiplayerMapPreview) { - delete MultiplayerMapPreview; + if (!Get_File_From_Host(preview_name, false)) { + DebugString("Preview file download failed\n"); + } else if (Load_Random_Map_Preview(preview_name)) { + InvalidateRect(WS_Top_Window(), NULL, FALSE); } - MultiplayerMapPreview = new MapPreviewClass; - MultiplayerMapPreview->Create_Preview_Surface(preview); - InvalidateRect(WS_Top_Window(), NULL, FALSE); - - DebugString("Cleaning up the temporary decompression buffers\n"); - delete [] preview; - delete [] buffer; Ipx.Set_Timing(TIMER_SECOND / 2, -1, 10 * TIMER_SECOND); } diff --git a/code/preview.cpp b/code/preview.cpp index a7ba40107..619ba05c0 100644 --- a/code/preview.cpp +++ b/code/preview.cpp @@ -544,8 +544,15 @@ unsigned * MapPreviewClass::Create_Paletted_Preview(int colorcount, int & size) /// arrives already packed, rather than being rendered from the map that is loaded. /// /// Pointer to the paletted preview block to expand. -void MapPreviewClass::Create_Preview_Surface(char * buffer) +/// Number of bytes the block occupies. +/// bool; Was a preview surface created? A block whose geometry does not fit in the +/// length given is refused and the current preview is left alone. +bool MapPreviewClass::Create_Preview_Surface(char * buffer, int length) { + if (buffer == NULL || length < (int)(sizeof(Header) + sizeof(int))) { + return(false); + } + int * header = (int *)buffer; int width = *header++; @@ -553,20 +560,38 @@ void MapPreviewClass::Create_Preview_Surface(char * buffer) int colorcount = *header; unsigned short *palette = (unsigned short *)header; + if (width <= 0 || height <= 0) { + return(false); + } + if (colorcount <= 0 || colorcount > MAX_COLOR_COUNT) { + return(false); + } + + int offset = (colorcount * sizeof(unsigned short)) + sizeof(Header) + sizeof(int); + + // Widened because the dimensions come off the wire and their product overflows an int. + long long const needed = (long long)offset + (long long)width * (long long)height; + if (needed > (long long)length) { + return(false); + } + if (SurfacePtr != NULL) { delete SurfacePtr; } SurfacePtr = new DSurface(width, height); SurfacePtr->Fill(TBLACK); - int offset = (colorcount * sizeof(unsigned short)) + sizeof(Header) + sizeof(int); unsigned char * indexptr = (unsigned char *)buffer + offset; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { - unsigned short entry = palette[*indexptr++ + 2]; + // The palette starts two shorts past the count that precedes it. + int index = *indexptr++; + unsigned short entry = (index < colorcount) ? palette[index + 2] : 0; int color = DSurface::Build_Hicolor_Pixel((entry >> 4) & 0x00F0, entry & 0x00F0, 16 * (entry & 0x000F)); SurfacePtr->Put_Pixel_Clip(Point2D(x, y), color, SurfacePtr->Get_Rect()); } } + + return(true); } diff --git a/code/preview.h b/code/preview.h index f63ba77fb..564958afd 100644 --- a/code/preview.h +++ b/code/preview.h @@ -34,7 +34,7 @@ class MapPreviewClass void Blit_Preview(HWND window); unsigned * Create_Paletted_Preview(int, int & size); - void Create_Preview_Surface(char * buffer); + bool Create_Preview_Surface(char * buffer, int length); XSurface * Get_Preview_Surface(void) { return(SurfacePtr); } private: @@ -51,6 +51,9 @@ class MapPreviewClass int Height; }; + static int const MAX_BLOCK_SIZE = 8 * 1024 * 1024; + static int const MAX_COLOR_COUNT = 4096; + private: /*