diff --git a/SConscript b/SConscript index ae2e890b4..64c6276b5 100644 --- a/SConscript +++ b/SConscript @@ -97,6 +97,11 @@ if not conf.CheckLib('bz2'): print "libbz2 not found or not installed" raise SystemExit(1) +#Check for zlib +if not conf.CheckLib('z'): + print "libz not found or not installed" + raise SystemExit(1) + if not conf.CheckCHeader("bzlib.h"): print "bzip2 headers not found" raise SystemExit(1) diff --git a/src/Format.cpp b/src/Format.cpp index 71a0e9eff..ef0050dfb 100644 --- a/src/Format.cpp +++ b/src/Format.cpp @@ -1,7 +1,10 @@ -#include +#include #include +#include +#include #include "Format.h" +#include "graphics/Graphics.h" std::string format::UnixtimeToDate(time_t unixtime, std::string dateFormat) { @@ -33,3 +36,261 @@ std::string format::UnixtimeToDateMini(time_t unixtime) return UnixtimeToDate(unixtime, "%H:%M:%S"); } } + +struct PNGChunk +{ + int Length; + char Name[4]; + char * Data; + + //char[4] CRC(); + + PNGChunk(int length, std::string name) + { + if(name.length()!=4) + throw std::runtime_error("Invalid chunk name"); + std::copy(name.begin(), name.begin()+4, Name); + Length = length; + if(length) + { + Data = new char[length]; + std::fill(Data, Data+length, 0); + } + else + { + Data = NULL; + } + } + unsigned long CRC() + { + if(!Data) + { + return format::CalculateCRC((unsigned char*)Name, 4); + } + else + { + unsigned char * temp = new unsigned char[4+Length]; + std::copy(Name, Name+4, temp); + std::copy(Data, Data+Length, temp+4); + unsigned long tempRet = format::CalculateCRC(temp, 4+Length); + delete[] temp; + return tempRet; + } + } + ~PNGChunk() + { + if(Data) + delete[] Data; + } +}; + +std::vector format::VideoBufferToPNG(const VideoBuffer & vidBuf) +{ + std::vector chunks; + + //Begin IHDR (Image header) chunk (Image size and depth) + PNGChunk IHDRChunk = PNGChunk(13, "IHDR"); + + //Image Width + IHDRChunk.Data[0] = (vidBuf.Width>>24)&0xFF; + IHDRChunk.Data[1] = (vidBuf.Width>>16)&0xFF; + IHDRChunk.Data[2] = (vidBuf.Width>>8)&0xFF; + IHDRChunk.Data[3] = (vidBuf.Width)&0xFF; + + //Image Height + IHDRChunk.Data[4] = (vidBuf.Height>>24)&0xFF; + IHDRChunk.Data[5] = (vidBuf.Height>>16)&0xFF; + IHDRChunk.Data[6] = (vidBuf.Height>>8)&0xFF; + IHDRChunk.Data[7] = (vidBuf.Height)&0xFF; + + //Bit depth + IHDRChunk.Data[8] = 8; //8bits per channel or 24bpp + + //Colour type + IHDRChunk.Data[9] = 2; //RGB triple + + //Everything else is default + chunks.push_back(&IHDRChunk); + + //Begin image data, format is 8bit RGB (24bit pixel) + int dataPos = 0; + unsigned char * uncompressedData = new unsigned char[(vidBuf.Width*vidBuf.Height*3)+vidBuf.Height]; + + //Byte ordering and filtering + unsigned char * previousRow = new unsigned char[vidBuf.Width*3]; + std::fill(previousRow, previousRow+(vidBuf.Width*3), 0); + unsigned char * currentRow = new unsigned char[vidBuf.Width*3]; + for(int y = 0; y < vidBuf.Height; y++) + { + int rowPos = 0; + for(int x = 0; x < vidBuf.Width; x++) + { + currentRow[rowPos++] = PIXR(vidBuf.Buffer[(y*vidBuf.Width)+x]); + currentRow[rowPos++] = PIXG(vidBuf.Buffer[(y*vidBuf.Width)+x]); + currentRow[rowPos++] = PIXB(vidBuf.Buffer[(y*vidBuf.Width)+x]); + } + + uncompressedData[dataPos++] = 2; //Up Sub(x) filter + for(int b = 0; b < rowPos; b++) + { + int filteredByte = (currentRow[b]-previousRow[b])&0xFF; + uncompressedData[dataPos++] = filteredByte; + } + + unsigned char * tempRow = previousRow; + previousRow = currentRow; + currentRow = tempRow; + } + delete[] currentRow; + delete[] previousRow; + + //Compression + int compressedBufferSize = (vidBuf.Width*vidBuf.Height*3)*2; + unsigned char * compressedData = new unsigned char[compressedBufferSize]; + + int result; + z_stream zipStream; + zipStream.zalloc = Z_NULL; + zipStream.zfree = Z_NULL; + zipStream.opaque = Z_NULL; + + result = deflateInit2(&zipStream, + 9, // level + Z_DEFLATED, // method + 10, // windowBits + 1, // memLevel + Z_DEFAULT_STRATEGY // strategy + ); + + if (result != Z_OK) exit(result); + + zipStream.next_in = uncompressedData; + zipStream.avail_in = dataPos; + + zipStream.next_out = compressedData; + zipStream.avail_out = compressedBufferSize; + + + result = deflate(&zipStream, Z_FINISH); + if (result != Z_STREAM_END) exit(result); + + int compressedSize = compressedBufferSize-zipStream.avail_out; + PNGChunk IDATChunk = PNGChunk(compressedSize, "IDAT"); + std::copy(compressedData, compressedData+compressedSize, IDATChunk.Data); + chunks.push_back(&IDATChunk); + + deflateEnd(&zipStream); + + delete[] compressedData; + delete[] uncompressedData; + + PNGChunk IENDChunk = PNGChunk(0, "IEND"); + chunks.push_back(&IENDChunk); + + //Write chunks to output buffer + int finalDataSize = 8; + for(std::vector::iterator iter = chunks.begin(), end = chunks.end(); iter != end; ++iter) + { + PNGChunk * cChunk = *iter; + finalDataSize += 4 + 4 + 4; + finalDataSize += cChunk->Length; + } + unsigned char * finalData = new unsigned char[finalDataSize]; + int finalDataPos = 0; + + //PNG File header + finalData[finalDataPos++] = 0x89; + finalData[finalDataPos++] = 0x50; + finalData[finalDataPos++] = 0x4E; + finalData[finalDataPos++] = 0x47; + finalData[finalDataPos++] = 0x0D; + finalData[finalDataPos++] = 0x0A; + finalData[finalDataPos++] = 0x1A; + finalData[finalDataPos++] = 0x0A; + + for(std::vector::iterator iter = chunks.begin(), end = chunks.end(); iter != end; ++iter) + { + PNGChunk * cChunk = *iter; + + //Chunk length + finalData[finalDataPos++] = (cChunk->Length>>24)&0xFF; + finalData[finalDataPos++] = (cChunk->Length>>16)&0xFF; + finalData[finalDataPos++] = (cChunk->Length>>8)&0xFF; + finalData[finalDataPos++] = (cChunk->Length)&0xFF; + + //Chunk name + std::copy(cChunk->Name, cChunk->Name+4, finalData+finalDataPos); + finalDataPos += 4; + + //Chunk data + if(cChunk->Data) + { + std::copy(cChunk->Data, cChunk->Data+cChunk->Length, finalData+finalDataPos); + finalDataPos += cChunk->Length; + } + + //Chunk CRC + unsigned long tempCRC = cChunk->CRC(); + finalData[finalDataPos++] = (tempCRC>>24)&0xFF; + finalData[finalDataPos++] = (tempCRC>>16)&0xFF; + finalData[finalDataPos++] = (tempCRC>>8)&0xFF; + finalData[finalDataPos++] = (tempCRC)&0xFF; + } + + std::vector outputData(finalData, finalData+finalDataPos); + + delete[] finalData; + + return outputData; +} + +//CRC functions, copypasta from W3 PNG spec. + +/* Table of CRCs of all 8-bit messages. */ +unsigned long crc_table[256]; + +/* Flag: has the table been computed? Initially false. */ +int crc_table_computed = 0; + +/* Make the table for a fast CRC. */ +void make_crc_table(void) +{ + unsigned long c; + int n, k; + + for (n = 0; n < 256; n++) { + c = (unsigned long) n; + for (k = 0; k < 8; k++) { + if (c & 1) + c = 0xedb88320L ^ (c >> 1); + else + c = c >> 1; + } + crc_table[n] = c; + } + crc_table_computed = 1; +} + +/* Update a running CRC with the bytes buf[0..len-1]--the CRC + should be initialized to all 1's, and the transmitted value + is the 1's complement of the final running CRC (see the + crc() routine below)). */ + +unsigned long update_crc(unsigned long crc, unsigned char *buf, int len) +{ + unsigned long c = crc; + int n; + + if (!crc_table_computed) + make_crc_table(); + for (n = 0; n < len; n++) + { + c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); + } + return c; +} + +unsigned long format::CalculateCRC(unsigned char * data, int len) +{ + return update_crc(0xffffffffL, data, len) ^ 0xffffffffL; +} \ No newline at end of file diff --git a/src/Format.h b/src/Format.h index 1df883f4d..fc55f8f32 100644 --- a/src/Format.h +++ b/src/Format.h @@ -1,7 +1,9 @@ #pragma once #include +#include +class VideoBuffer; namespace format { template std::string NumberToString(T number) @@ -20,4 +22,6 @@ namespace format std::string UnixtimeToDate(time_t unixtime, std::string dateFomat = "%d %b %Y"); std::string UnixtimeToDateMini(time_t unixtime); + std::vector VideoBufferToPNG(const VideoBuffer & vidBuf); + unsigned long CalculateCRC(unsigned char * data, int length); } \ No newline at end of file diff --git a/src/cat/LuaScriptInterface.cpp b/src/cat/LuaScriptInterface.cpp index 97c323c19..b37633dbe 100644 --- a/src/cat/LuaScriptInterface.cpp +++ b/src/cat/LuaScriptInterface.cpp @@ -7,8 +7,11 @@ #include #include "Config.h" +#include "Format.h" #include "LuaScriptInterface.h" #include "TPTScriptInterface.h" +#include "dialogues/ErrorMessage.h" +#include "dialogues/InformationMessage.h" #include "simulation/Simulation.h" #include "game/GameModel.h" #include "LuaScriptHelper.h" @@ -258,7 +261,7 @@ bool LuaScriptInterface::OnMouseWheel(int x, int y, int d) bool LuaScriptInterface::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool alt) { - int modifiers; + int modifiers = 0; if(shift) modifiers |= 0x001; if(ctrl) @@ -270,7 +273,7 @@ bool LuaScriptInterface::OnKeyPress(int key, Uint16 character, bool shift, bool bool LuaScriptInterface::OnKeyRelease(int key, Uint16 character, bool shift, bool ctrl, bool alt) { - int modifiers; + int modifiers = 0; if(shift) modifiers |= 0x001; if(ctrl) @@ -994,16 +997,8 @@ int luatpt_graphics_func(lua_State *l) int luatpt_error(lua_State* l) { - /*char *error = ""; - error = mystrdup((char*)luaL_optstring(l, 1, "Error text")); - if(vid_buf!=NULL){ - error_ui(vid_buf, 0, error); - free(error); - return 0; - } - free(error); - return luaL_error(l, "Screen buffer does not exist");*/ - //TODO IMPLEMENT + std::string errorMessage = std::string(luaL_optstring(l, 1, "Error text")); + new ErrorMessage("Error", errorMessage); return 0; } int luatpt_drawtext(lua_State* l) @@ -1539,7 +1534,7 @@ int luatpt_get_property(lua_State* l) int luatpt_drawpixel(lua_State* l) { - /*int x, y, r, g, b, a; + int x, y, r, g, b, a; x = luaL_optint(l, 1, 0); y = luaL_optint(l, 2, 0); r = luaL_optint(l, 3, 255); @@ -1557,12 +1552,8 @@ int luatpt_drawpixel(lua_State* l) if (b>255) b = 255; if (a<0) a = 0; if (a>255) a = 255; - if (luacon_g->vid!=NULL) - { - luacon_g->drawpixel(x, y, r, g, b, a); - return 0; - }*/ - return luaL_error(l, "Deprecated"); + luacon_g->blendpixel(x, y, r, g, b, a); + return 0; } int luatpt_drawrect(lua_State* l) @@ -1672,10 +1663,6 @@ int luatpt_get_name(lua_State* l) int luatpt_set_shortcuts(lua_State* l) { - /*int state; - state = luaL_optint(l, 1, 0); - sys_shortcuts = (state==0?0:1); - return 0;*/ return luaL_error(l, "set_shortcuts: deprecated"); } @@ -1879,20 +1866,9 @@ int luatpt_input(lua_State* l) } int luatpt_message_box(lua_State* l) { - /*char *title, *text; - title = mystrdup(luaL_optstring(l, 1, "Title")); - text = mystrdup(luaL_optstring(l, 2, "Message")); - if (vid_buf!=NULL) - { - info_ui(vid_buf, title, text); - free(title); - free(text); - return 0; - } - free(title); - free(text); - return luaL_error(l, "Screen buffer does not exist");;*/ - //TODO IMPLEMENT + std::string title = std::string(luaL_optstring(l, 1, "Title")); + std::string message = std::string(luaL_optstring(l, 2, "Message")); + new InformationMessage(title, message); return 0; } int luatpt_get_numOfParts(lua_State* l) @@ -1945,15 +1921,12 @@ int luatpt_hud(lua_State* l) } int luatpt_gravity(lua_State* l) { - //luacon_sim-> - /*int gravstate; + int gravstate; gravstate = luaL_optint(l, 1, 0); if(gravstate) - start_grav_async(); + luacon_sim->grav->start_grav_async(); else - stop_grav_async(); - ngrav_enable = (gravstate==0?0:1);*/ - //TODO IMPLEMENT + luacon_sim->grav->stop_grav_async(); return 0; } int luatpt_airheat(lua_State* l) @@ -1991,7 +1964,7 @@ int luatpt_heat(lua_State* l) int luatpt_cmode_set(lua_State* l) { //TODO IMPLEMENT - return luaL_error(l, "Not implemented"); + return luaL_error(l, "cmode_set: Deprecated"); } int luatpt_setfire(lua_State* l) { @@ -2002,11 +1975,7 @@ int luatpt_setfire(lua_State* l) } int luatpt_setdebug(lua_State* l) { - /*int debug = luaL_optint(l, 1, 0); - debug_flags = debug; - return 0;*/ - //TODO IMPLEMENT - return luaL_error(l, "Not implemented"); + return luaL_error(l, "setdebug: Deprecated"); } int luatpt_setfpscap(lua_State* l) { @@ -2108,32 +2077,30 @@ fin: int luatpt_setwindowsize(lua_State* l) { - /*int result, scale = luaL_optint(l,1,1), kiosk = luaL_optint(l,2,0); + int scale = luaL_optint(l,1,1), kiosk = luaL_optint(l,2,0); if (scale!=2) scale = 1; if (kiosk!=1) kiosk = 0; - result = set_scale(scale, kiosk); - lua_pushnumber(l, result); - return 1;*/ - //TODO Implement - return luaL_error(l, "Not implemented"); + ui::Engine::Ref().SetScale(scale); + ui::Engine::Ref().SetFullscreen(kiosk); + return 0; } int luatpt_screenshot(lua_State* l) { //TODO Implement - /*int captureUI = luaL_optint(l, 1, 0); - if(vid_buf) + int captureUI = luaL_optint(l, 1, 0); + std::vector data; + if(captureUI) { - if(captureUI) - { - dump_frame(vid_buf, XRES+BARSIZE, YRES+MENUSIZE, XRES+BARSIZE); - } - else - { - dump_frame(vid_buf, XRES, YRES, XRES+BARSIZE); - } - return 0; - }*/ - return luaL_error(l, "Not implemented"); + VideoBuffer screenshot(ui::Engine::Ref().g->DumpFrame()); + data = format::VideoBufferToPNG(screenshot); + } + else + { + VideoBuffer screenshot(luacon_ren->DumpFrame()); + data = format::VideoBufferToPNG(screenshot); + } + Client::Ref().WriteFile(data, "screenshot.png"); + return 0; } diff --git a/src/graphics/Graphics.cpp b/src/graphics/Graphics.cpp index eb924355a..e7cce03da 100644 --- a/src/graphics/Graphics.cpp +++ b/src/graphics/Graphics.cpp @@ -909,3 +909,12 @@ void Graphics::draw_image(VideoBuffer * vidBuf, int x, int y, int a) draw_image(vidBuf->Buffer, x, y, vidBuf->Width, vidBuf->Height, a); } +VideoBuffer Graphics::DumpFrame() +{ +#ifdef OGLI +#else + VideoBuffer newBuffer(XRES+BARSIZE, YRES+MENUSIZE); + std::copy(vid, vid+((XRES+BARSIZE)*(YRES+MENUSIZE)), newBuffer.Buffer); + return newBuffer; +#endif +} \ No newline at end of file diff --git a/src/graphics/Graphics.h b/src/graphics/Graphics.h index e74239e99..46d28b4d7 100644 --- a/src/graphics/Graphics.h +++ b/src/graphics/Graphics.h @@ -4,6 +4,7 @@ #include #include #include +#include #if defined(OGLI) #include "OpenGLHeaders.h" #endif @@ -135,6 +136,8 @@ public: static int textwidth(const char *s); static void textsize(const char * s, int & width, int & height); + VideoBuffer DumpFrame(); + void Acquire(); void Release(); diff --git a/src/graphics/Renderer.cpp b/src/graphics/Renderer.cpp index 11cf9c3ff..6c1a31ba4 100644 --- a/src/graphics/Renderer.cpp +++ b/src/graphics/Renderer.cpp @@ -2475,6 +2475,23 @@ unsigned int Renderer::GetColourMode() return colour_mode; } +VideoBuffer Renderer::DumpFrame() +{ +#ifdef OGLR +#elif defined(OGLI) + VideoBuffer newBuffer(XRES, YRES); + std::copy(vid, vid+(XRES*YRES), newBuffer.Buffer); + return newBuffer; +#else + VideoBuffer newBuffer(XRES, YRES); + for(int y = 0; y < YRES; y++) + { + std::copy(vid+(y*(XRES+BARSIZE)), vid+(y*(XRES+BARSIZE))+XRES, newBuffer.Buffer+(y*XRES)); + } + return newBuffer; +#endif +} + Renderer::~Renderer() { #if !defined(OGLR) diff --git a/src/graphics/Renderer.h b/src/graphics/Renderer.h index 6691ef1b9..cc7debeb6 100644 --- a/src/graphics/Renderer.h +++ b/src/graphics/Renderer.h @@ -113,6 +113,8 @@ public: void draw_image(pixel *img, int x, int y, int w, int h, int a); #endif + VideoBuffer DumpFrame(); + void drawblob(int x, int y, unsigned char cr, unsigned char cg, unsigned char cb); //... //Display mode modifiers