local ffi = require("ffi")
local sampBase = getModuleHandle("samp.dll")
ffi.cdef[[
typedef struct tagBITMAPINFOHEADER {
uint32_t biSize;
int32_t biWidth;
int32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
} BITMAPINFOHEADER;
typedef struct tagRGBQUAD {
uint8_t rgbBlue;
uint8_t rgbGreen;
uint8_t rgbRed;
uint8_t rgbReserved;
} RGBQUAD;
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO;
typedef struct HDC__ HDC;
HDC* GetDC(void* hWnd);
int ReleaseDC(void* hWnd, HDC* hDC);
int GetDeviceCaps(HDC* hdc, int nIndex);
HDC* CreateCompatibleDC(HDC* hdc);
void* CreateCompatibleBitmap(HDC* hdc, int32_t nWidth, int32_t nHeight);
void* SelectObject(HDC* hdc, void* h);
int BitBlt(HDC* hdcDest, int32_t nXDest, int32_t nYDest, int32_t nWidth, int32_t nHeight, HDC* hdcSrc, int32_t nXSrc, int32_t nYSrc, uint32_t dwRop);
int GetDIBits(HDC* hdc, void* hbmp, uint32_t uStartScan, uint32_t cScanLines, void* lpvBits, BITMAPINFO* lpbi, uint32_t uUsage);
int DeleteDC(HDC* hdc);
int DeleteObject(void* hObject);
]]
local function saveBMP(filename, buffer, width, height)
local file = io.open(filename, "wb")
if file then
local header = string.char(0x42, 0x4D)
local fileSize = 14 + 40 + width * height * 3
local offset = 14 + 40
file:write(header)
file:write(string.char(fileSize % 256, math.floor(fileSize / 256) % 256, math.floor(fileSize / 65536) % 256, math.floor(fileSize / 16777216)))
file:write(string.rep("\0", 4))
file:write(string.char(offset % 256, math.floor(offset / 256) % 256, math.floor(offset / 65536) % 256, math.floor(offset / 16777216)))
file:write(string.char(40, 0, 0, 0)) -- BITMAPINFOHEADER size
file:write(string.char(width % 256, math.floor(width / 256) % 256, math.floor(width / 65536) % 256, math.floor(width / 16777216)))
file:write(string.char(height % 256, math.floor(height / 256) % 256, math.floor(height / 65536) % 256, math.floor(height / 16777216)))
file:write(string.char(1, 0))
file:write(string.char(24, 0))
file:write(string.rep("\0", 24))
file:write(buffer)
file:close()
return true
else
return false
end
end
local function takeScreenshot()
local desktopDC = ffi.C.GetDC(nil)
local screenWidth = ffi.C.GetDeviceCaps(desktopDC, 8)
local screenHeight = ffi.C.GetDeviceCaps(desktopDC, 10)
local compatibleDC = ffi.C.CreateCompatibleDC(desktopDC)
local bitmap = ffi.C.CreateCompatibleBitmap(desktopDC, screenWidth, screenHeight)
ffi.C.SelectObject(compatibleDC, bitmap)
ffi.C.BitBlt(compatibleDC, 0, 0, screenWidth, screenHeight, desktopDC, 0, 0, 13369376)
local bi = ffi.new("BITMAPINFO")
bi.bmiHeader.biSize = ffi.sizeof("BITMAPINFOHEADER")
bi.bmiHeader.biWidth = screenWidth
bi.bmiHeader.biHeight = screenHeight
bi.bmiHeader.biPlanes = 1
bi.bmiHeader.biBitCount = 24
bi.bmiHeader.biCompression = 0
bi.bmiHeader.biSizeImage = 0
bi.bmiHeader.biXPelsPerMeter = 0
bi.bmiHeader.biYPelsPerMeter = 0
bi.bmiHeader.biClrUsed = 0
bi.bmiHeader.biClrImportant = 0
local buffer = ffi.new("uint8_t[?]", screenWidth * screenHeight * 3)
ffi.C.GetDIBits(desktopDC, bitmap, 0, screenHeight, buffer, bi, 0)
local documentsPath = os.getenv("USERPROFILE") .. "\\Documents\\GTA San Andreas User Files\\SAMP\\screens\\"
local fileNameBase = "sa-mp-%03i.png"
local fileName = ""
local index = 0
repeat
index = index + 1
fileName = documentsPath .. string.format(fileNameBase, index)
until not doesFileExist(fileName)
if saveBMP(fileName, ffi.string(buffer, screenWidth * screenHeight * 3), screenWidth, screenHeight) then
sampAddChatMessage("Screenshot Taken: "..string.format(fileNameBase, index), -1)
else
sampAddChatMessage("Failed to save screenshot.", -1)
end
ffi.C.DeleteObject(bitmap)
ffi.C.DeleteDC(compatibleDC)
ffi.C.ReleaseDC(nil, desktopDC)
end
local function doesFileExist(fileName)
local file = io.open(fileName, "r")
if file then
file:close()
return true
end
return false
end
function main()
while not isSampAvailable() do wait(100) end
--nop original take screenshot
if get_samp_version() == "r1" then
writeMemory(sampBase+0x7115D, 4, 0x90909090, true)
writeMemory(sampBase+0x7115D+4, 1, 0x90, true)
elseif get_samp_version() == "r3" then
writeMemory(sampBase+0x7504D, 4, 0x90909090, true)
writeMemory(sampBase+0x7504D+4, 1, 0x90, true)
end
while true do
wait(0)
if isKeyJustPressed(0x77) then
takeScreenshot()
end
end
end
addEventHandler('onScriptTerminate', function(scr)
if scr == thisScript() then
--return original take screenshot
if get_samp_version() == "r1" then
writeMemory(sampBase+0x7115D, 4, 0xFFFE5EE8, true)
writeMemory(sampBase+0x7115D+4, 1, 0xFF, true)
elseif get_samp_version() == "r3" then
writeMemory(sampBase+0x7504D, 4, 0xFFFE5EE8, true)
writeMemory(sampBase+0x7504D+4, 1, 0xFF, true)
end
end
end)
function get_samp_version()
local e_lfanew = ffi.cast("long*", sampBase + 60)[0]
local nt_header = sampBase + e_lfanew
local entry_point_addr = ffi.cast("unsigned int*", nt_header + 40)[0]
local versions = {
[0x31DF13] = "r1",
[0x3195DD] = "r2",
[0xCC4D0] = "r3",
[0xCBCB0] = "r4",
[0xFDB60] = "dl"
}
return versions[entry_point_addr] or "unknown"
end