Last active
April 14, 2021 04:16
-
-
Save x4fx77x4f/384f6f7a09f8a3bc50b8bbcb42a63a33 to your computer and use it in GitHub Desktop.
Utility to easily create pixel perfect captures of StarfallEx chips
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- The below line is the *only* one you need to change. | |
--@include chopper.txt | |
-- The following are optional. | |
local OVERRIDE_WIDTH = false and 320 | |
local OVERRIDE_HEIGHT = false and 240 | |
local OVERRIDE_RT = false and 'out' -- Name of rendertarget to save, or none to save the screen | |
local OVERRIDE_DELAY = false and 2 -- How many seconds to wait until capturing | |
local THRESHOLD = quotaMax()*0.9 | |
-- End configuration. | |
--@client | |
-- I only *need* to cache the ones I'm detouring, but of course, I couldn't stop there... | |
local _coroutine = coroutine | |
local _coroutine_resume = _coroutine.resume | |
local _coroutine_yield = _coroutine.yield | |
local _file = file | |
local _file_exists = _file.exists | |
local _hasPermission = hasPermission | |
local _hook = hook | |
local _hook_add = _hook.add | |
local _hook_remove = _hook.remove | |
local _hook_run = _hook.run | |
local _math = math | |
local _math_floor = _math.floor | |
local _math_max = _math.max | |
local _pairs = pairs | |
local _quotaAverage = quotaAverage | |
local _quotaTotalAverage = quotaTotalAverage | |
local _quotaTotalUsed = quotaTotalUsed | |
local _quotaUsed = quotaUsed | |
local _render = render | |
local _render_capturePixels = _render.capturePixels | |
local _render_clear = _render.clear | |
local _render_drawRect = _render.drawRect | |
local _render_drawText = _render.drawText | |
local _render_drawTexturedRectUV = _render.drawTexturedRectUV | |
local _render_getTextSize = _render.getTextSize | |
local _render_readPixel = _render.readPixel | |
local _render_selectRenderTarget = _render.selectRenderTarget | |
local _render_setBackgroundColor = _render.setBackgroundColor | |
local _render_setFilterMag = _render.setFilterMag | |
local _render_setFilterMin = _render.setFilterMin | |
local _render_setFont = _render.setFont | |
local _render_setRenderTargetTexture = _render.setRenderTargetTexture | |
local _render_setRGBA = _render.setRGBA | |
local _setupPermissionRequest = setupPermissionRequest | |
local _string = string | |
local _string_find = _string.find | |
local _string_sub = _string.sub | |
local _TEXFILTER_POINT = TEXFILTER.POINT | |
local _timer = timer | |
local _timer_create = _timer.create | |
local _timer_remove = _timer.remove | |
local _timer_simple = _timer.simple | |
local _timer_systime = _timer.systime | |
local message | |
local permissions = { | |
'file.exists', | |
'file.open', | |
'render.offscreen', | |
'render.screen' | |
} | |
local function init() | |
for _, permission in _pairs(permissions) do | |
if not _hasPermission(permission) then | |
_setupPermissionRequest(permissions, "a", true) | |
return | |
end | |
end | |
_hook_remove('permissionrequest', '') | |
_hook_remove('starfallused', '') | |
message = "Waiting..." | |
local function patch_hookname(name) | |
return _string_sub(name, 1, 1) == '_' and '_'..name or name == 'render' and '_render' or name | |
end | |
local hooks = {} | |
function _hook.add(hookname, name, func) | |
hookname = patch_hookname(hookname) | |
local hooks_hookname = hooks[hookname] | |
if not hooks_hookname then | |
hooks_hookname = {} | |
hooks[hookname] = hooks_hookname | |
end | |
hooks_hookname[name] = true | |
return _hook_add(hookname, name, func) | |
end | |
function _hook.remove(hookname, name) | |
hookname = patch_hookname(hookname) | |
local hooks_hookname = hooks[hookname] | |
if hooks_hookname then | |
hooks_hookname[name] = nil | |
end | |
return _hook_remove(patch_hookname(hookname), name) | |
end | |
function _hook.run(hookname, ...) | |
return _hook_run(patch_hookname(hookname), ...) | |
end | |
local function patch_rt_name(name) | |
return _string_sub(name, 1, 1) == '_' and '_'..name | |
end | |
function _render.selectRenderTarget(name) | |
return _render_selectRenderTarget(name == nil and '_screengrab' or name) | |
end | |
local bg = Color(0, 0, 0, 255) | |
function _render.setBackgroundColor(color) | |
bg = color | |
end | |
local timers = {} | |
local timers_enabled = true | |
function _timer.create(name, delay, reps, func) | |
timers[name] = true | |
return _timer_create(name, delay, reps, func) | |
end | |
function _timer.remove(name) | |
timers[name] = nil | |
return _timer_remove(name) | |
end | |
function _timer.simple(delay, func) | |
return _timer_simple(delay, function() | |
return timers_enabled and func() | |
end) | |
end | |
local i, path = 1 | |
repeat | |
--path = 'screengrab/'..i..'.ppm.dat' | |
path = 'screengrab/'..i..'.pam.dat' | |
i = i+1 | |
until not _file_exists(path) | |
local handle = assert(_file.open(path, 'wb'), "failed to open "..path) | |
local w, h = OVERRIDE_WIDTH or 512, OVERRIDE_HEIGHT or 512 | |
--handle:write('P6 '..w..' '..h..' 255\n') | |
handle:write('P7\nWIDTH '..w..'\nHEIGHT '..h..'\nDEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n') | |
local i, j = 0, w*h | |
local thread = _coroutine.create(function() | |
for y=0, h-1 do | |
for x=0, w-1 do | |
i = i+1 | |
local pixel = _render_readPixel(x, y) | |
handle:writeByte(pixel[1]) | |
handle:writeByte(pixel[2]) | |
handle:writeByte(pixel[3]) | |
handle:writeByte(pixel[4]) -- PAM only | |
_coroutine_yield() | |
end | |
end | |
return true | |
end) | |
local duedate = OVERRIDE_DELAY and _timer_systime()+OVERRIDE_DELAY or 0 | |
_hook_add('render', '', function(screen) | |
_render_selectRenderTarget('_screengrab') | |
_render_clear(bg, false) | |
_hook_run('_render', screen) | |
if _timer_systime() < duedate then | |
return | |
end | |
_hook_remove('render', '') | |
_render_selectRenderTarget(OVERRIDE_RT or '_screengrab') | |
_render_capturePixels() | |
for hookname, hooks in _pairs(hooks) do | |
for name in _pairs(hooks) do | |
_hook_remove(hookname, name) | |
end | |
end | |
for name in _pairs(timers) do | |
_timer_remove(name) | |
end | |
timers_enabled = false | |
_hook_add('renderoffscreen', '', function() | |
while _math_max(_quotaUsed(), _quotaAverage(), _quotaTotalUsed(), _quotaTotalAverage()) < THRESHOLD do | |
_render_selectRenderTarget(OVERRIDE_RT or '_screengrab') | |
if _coroutine_resume(thread) then | |
handle:close() | |
_hook_remove('renderoffscreen', '') | |
message = "Complete" | |
return | |
end | |
end | |
message = "Capturing... (".._math_floor(i/j*100).."%)" | |
end) | |
end) | |
for path in _pairs(getScripts()) do | |
if path ~= 'screengrab.txt' and path ~= 'screengrab.lua' and (_string_find(path, '%.txt$') or _string_find(path, '%.lua$')) then | |
setName(path) | |
dofile(path) | |
return | |
end | |
end | |
error("no include") | |
end | |
message = "Permission?" | |
local bg = Color(0, 0, 0, 255) | |
_render.createRenderTarget('_screengrab') | |
_hook_add('render', 'status', function() | |
_render_setBackgroundColor(bg) | |
_render_setRGBA(255, 255, 255, 255) | |
_render_setRenderTargetTexture(OVERRIDE_RT or '_screengrab') | |
--_render.drawTexturedRect(0, 0, 512, 512) | |
_render_drawTexturedRectUV(0, 0, 512, 512, 0, 0, OVERRIDE_WIDTH and OVERRIDE_WIDTH/1024 or 0.5, OVERRIDE_HEIGHT and OVERRIDE_HEIGHT/1024 or 0.5) | |
_render_setFont('DermaLarge') | |
local w, h = _render_getTextSize(message) | |
_render_setRGBA(0, 0, 0, 255) | |
--_render_drawRect(8, 8, w, h) | |
_render_drawText(10, 10, message) | |
_render_setRGBA(255, 255, 255, 255) | |
_render_drawText(8, 8, message) | |
end) | |
_hook_add('permissionrequest', '', init) | |
local me = player() | |
_hook_add('starfallused', '', function(activator, used) | |
if activator == me then | |
init() | |
end | |
end) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sample screenshots:




Helicopter demo:
CHIP-8 emulator:
rttransparency.txt
demonstrating a Linux-only alpha blending bug:Super Mario 64 intro test utilizing custom overrides: