This commit is contained in:
Bill Niblock 2024-10-29 19:44:41 -04:00
parent 1ef93799d1
commit 2df9b96765
98 changed files with 30423 additions and 0 deletions

0
assets/.keep Normal file
View file

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

166
buildtools/appimage/build.sh Executable file
View file

@ -0,0 +1,166 @@
#!/bin/bash
set -eo >/dev/null
CURRENT_APPIMAGEKIT_RELEASE=9
ARCH="$(uname -m)"
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <version>"
exit 0
fi
VERSION="$1"
LOVEFILE="$2"
USERNAME=$3
TOKEN=$4
# https://github.com/love2d/love/releases/download/11.5/love-11.5-x86_64.AppImage
# https://github.com/love2d/love/releases/download/11.4/love-11.4-x86_64.AppImage
# https://github.com/love2d/love/releases/download/11.3/love-11.3-x86_64.AppImage
# https://github.com/love2d/love/releases/download/11.2/love-11.2-x86_64.AppImage
# https://github.com/love2d/love/releases/download/11.1/love-11.1-linux-x86_64.AppImage
LOVE_AppImage_URL=https://github.com/love2d/love/releases/download/${VERSION}/love-${VERSION}-${ARCH}.AppImage
if [ $VERSION == "11.1" ]
then
LOVE_AppImage_URL=https://github.com/love2d/love/releases/download/${VERSION}/love-${VERSION}-linux-${ARCH}.AppImage
fi
CACHE_DIR=${HOME}/.cache/love-release/love
LOVE_AppImage=$CACHE_DIR/love-${VERSION}-${ARCH}.AppImage
if ! test -d ${CACHE_DIR}; then
mkdir -p ${CACHE_DIR}
fi
get-development-artifact(){
owner=love2d
repo=love
branch="12.0-development"
artifact="love-x86_64.AppImage" # love-windows-x64 love-macos
token=$2
username=$1
list_run_url="https://api.github.com/repos/${owner}/${repo}/actions/runs"
echo "find latest run of branch 12.0"
latest_run=$(curl -v --silent \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization token ${token}" "${list_run_url}" \
--stderr - |\
grep -B 4 "\"head_branch\": \"12.0-development\"" |\
grep "id" |\
head -1 |\
sed 's/^.* \(.*\),$/\1/g')
list_artifacts_url="https://api.github.com/repos/${owner}/${repo}/actions/runs/${latest_run}/artifacts"
echo "get artifact_url"
artifact_url=$(curl --silent \
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization token ${token}" \
"${list_artifacts_url}" \
--stderr - |\
grep -A 5 "\"name\": \"${artifact}\"," |\
grep "archive_download_url" |\
head -1 |\
sed 's/^.* \"\(.*\)\",$/\1/g')
echo "download target"
echo curl -L -u ${username}:${token} ${artifact_url} -o love.zip
echo $(pwd)
rm -f love.zip love-*.AppImage && \
curl -L -u ${username}:${token} ${artifact_url} -o love.zip \
&& unzip love.zip && \
rm -f love.zip ${artifact} && \
mv love-*.AppImage ${artifact}
}
if ! test -f ${LOVE_AppImage}; then
if [ $VERSION != "12.0" ]
then
curl -L -C - -o ${LOVE_AppImage} ${LOVE_AppImage_URL}
else
get-development-artifact $USERNAME $TOKEN
mv love-x86_64.AppImage ${LOVE_AppImage}
fi
chmod +x ${LOVE_AppImage}
fi
echo $CACHE_DIR
if ! test -f ${LOVE_AppImage}; then
echo "No tarball found for $VERSION in $LOVE_AppImage"
exit 1
fi
download_if_needed() {
if ! test -f "$1"; then
if ! curl -L -o "$1" "https://github.com/AppImage/AppImageKit/releases/download/${CURRENT_APPIMAGEKIT_RELEASE}/$1"; then
echo "Failed to download appimagetool"
echo "Please supply it manually"
exit 1
fi
chmod +x "$1"
fi
}
main() {
download_if_needed appimagetool-${ARCH}.AppImage
# Extract the tarball build into a folder
rm -rf love-prepared
mkdir -p love-prepared
cd love-prepared
$LOVE_AppImage --appimage-extract 1> /dev/null 2> /dev/null
mv squashfs-root/* .
rm squashfs-root/.DirIcon
rmdir squashfs-root
# Add our small wrapper script (yay, more wrappers), and AppRun
local desktopfile="love.desktop"
local icon="love"
local target="love-${VERSION}"
if test -f ../../game.desktop.in; then
desktopfile="game.desktop"
cp ../../game.desktop.in .
fi
if test -f ../../game.svg; then
icon="game"
cp ../../game.svg .
fi
if test -f ${LOVEFILE}; then
if [[ $VERSION == "11.4" || $VERSION == "11.5" || $VERSION == "12.0" ]]
then
dir="bin"
else
dir="usr/bin"
fi
target="game"
cat $dir/love ${LOVEFILE} > $dir/love-fused
mv $dir/love-fused $dir/love
chmod +x $dir/love
else
echo "Love file ${LOVEFILE} not found"
fi
# Add our desktop file
mv ${desktopfile} ${desktopfile}.in
sed -e "s/%ICON%/${icon}/" "${desktopfile}.in" > "$desktopfile"
rm "${desktopfile}.in"
# Add a DirIcon
cp "${icon}.svg" .DirIcon
# Clean up
if test -f ../../game.desktop.in; then
rm love.desktop.in
fi
if test -f ../../game.svg; then
rm love.svg
fi
# Now build the final AppImage
cd ..
# ./love-prepared/AppRun -n love-prepared "${target}-${ARCH}.AppImage"
# Work around missing FUSE/docker
./appimagetool-${ARCH}.AppImage --appimage-extract 1> /dev/null 2> /dev/null
./squashfs-root/AppRun -n love-prepared "${target}-${ARCH}.AppImage" 1> /dev/null 2> /dev/null
## rm -rf love-prepared/
}
main "$@"

View file

@ -0,0 +1,962 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="141.73px" height="141.73px" viewBox="0 0 141.73 141.73" enable-background="new 0 0 141.73 141.73" xml:space="preserve">
<g>
<g opacity="0.3">
<path d="M117.726,21.413c-0.016-0.017-0.035-0.027-0.053-0.042C96.307,3.014,66.118-0.243,41.136,12.523
C16.168,25.282,1.956,53.733,5.382,81.306c2.392,19.268,13.129,36.165,28.466,47.021c0,0,42.277-37.776,53.504-51.181
c10.53-12.582,33.52-52.764,33.52-52.764C119.861,23.365,118.814,22.374,117.726,21.413z"/>
</g>
<g>
<path fill="#E74A99" d="M115.726,18.413c-0.016-0.017-0.035-0.027-0.053-0.042C94.307,0.014,64.118-3.243,39.136,9.523
C14.168,22.282-0.044,50.733,3.382,78.306c2.392,19.268,13.129,36.165,28.466,47.021c0,0,42.277-37.776,53.504-51.181
c10.53-12.582,33.52-52.764,33.52-52.764C117.861,20.365,116.814,19.374,115.726,18.413z"/>
</g>
</g>
<g>
<g opacity="0.3">
<path d="M33.849,128.327c7.067,5.001,15.11,8.726,23.765,10.801c27.076,6.487,56.27-5.293,71.759-28.261
c18.256-27.077,14.241-63.649-8.5-86.485c0,0-32.464,38.778-43.287,51.706C66.65,89.146,33.849,128.327,33.849,128.327z"/>
</g>
<g>
<path fill="#27AAE1" d="M31.849,125.327c7.067,5.001,15.11,8.726,23.765,10.801c27.076,6.487,56.27-5.293,71.759-28.261
c18.256-27.077,14.241-63.649-8.5-86.485c0,0-32.464,38.778-43.287,51.706C64.65,86.146,31.849,125.327,31.849,125.327z"/>
</g>
</g>
<g>
<image overflow="visible" opacity="0.3" width="354" height="324" xlink:href="data:image/png;base64,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
rkJggg==" transform="matrix(0.24 0 0 0.24 28.4971 38.3643)">
</image>
<g>
<path fill="#FFFFFF" d="M73.043,52.8c0,0-6.48-14.187-19.968-10.958c-13.486,3.23-41.652,30.792,17.853,65.083H70.88
c59.504-34.291,31.34-61.852,17.853-65.083C75.245,38.613,68.766,52.8,68.766,52.8H73.043z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 72 KiB

View file

@ -0,0 +1,33 @@
#!/bin/sh
if [ -z "$APPDIR" ]; then
APPDIR="$(dirname "$(readlink -f "$0")")"
fi
export LD_LIBRARY_PATH="$APPDIR/lib/:$LD_LIBRARY_PATH"
if [ -z "$XDG_DATA_DIRS" ]; then #unset or empty
XDG_DATA_DIRS="/usr/local/share/:/usr/share/"
fi
export XDG_DATA_DIRS="$APPDIR/share/:$XDG_DATA_DIRS"
if [ -z "$LUA_PATH" ]; then
LUA_PATH=";" # so ends with ;;
fi
# if user's LUA_PATH does not end with ;; then user doesn't want the default path ?
export LUA_PATH="$APPDIR/share/luajit-2.1.0-beta3/?.lua;$APPDIR/share/lua/5.1/?.lua;$LUA_PATH"
if [ -z "$LUA_CPATH" ]; then
LUA_CPATH=";"
fi
export LUA_CPATH="$APPDIR/lib/lua/5.1/?.so;$LUA_CPATH"
# uncomment and edit to add your own game
#FUSE_PATH="$APPDIR/my_game.love"
#FUSE_PATH="$APPDIR/my_game"
if [ -z "$FUSE_PATH" ]; then
exec "$APPDIR/bin/love" "$@"
else
exec "$APPDIR/bin/love" --fused "$FUSE_PATH" "$@"
fi

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
[Desktop Entry]
Name=LÖVE
Comment=The unquestionably awesome 2D game engine
MimeType=application/x-love-game;
Exec=love %f
Type=Application
Categories=Development;Game;
Terminal=false
Icon=love
NoDisplay=true

View file

@ -0,0 +1,962 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="141.73px" height="141.73px" viewBox="0 0 141.73 141.73" enable-background="new 0 0 141.73 141.73" xml:space="preserve">
<g>
<g opacity="0.3">
<path d="M117.726,21.413c-0.016-0.017-0.035-0.027-0.053-0.042C96.307,3.014,66.118-0.243,41.136,12.523
C16.168,25.282,1.956,53.733,5.382,81.306c2.392,19.268,13.129,36.165,28.466,47.021c0,0,42.277-37.776,53.504-51.181
c10.53-12.582,33.52-52.764,33.52-52.764C119.861,23.365,118.814,22.374,117.726,21.413z"/>
</g>
<g>
<path fill="#E74A99" d="M115.726,18.413c-0.016-0.017-0.035-0.027-0.053-0.042C94.307,0.014,64.118-3.243,39.136,9.523
C14.168,22.282-0.044,50.733,3.382,78.306c2.392,19.268,13.129,36.165,28.466,47.021c0,0,42.277-37.776,53.504-51.181
c10.53-12.582,33.52-52.764,33.52-52.764C117.861,20.365,116.814,19.374,115.726,18.413z"/>
</g>
</g>
<g>
<g opacity="0.3">
<path d="M33.849,128.327c7.067,5.001,15.11,8.726,23.765,10.801c27.076,6.487,56.27-5.293,71.759-28.261
c18.256-27.077,14.241-63.649-8.5-86.485c0,0-32.464,38.778-43.287,51.706C66.65,89.146,33.849,128.327,33.849,128.327z"/>
</g>
<g>
<path fill="#27AAE1" d="M31.849,125.327c7.067,5.001,15.11,8.726,23.765,10.801c27.076,6.487,56.27-5.293,71.759-28.261
c18.256-27.077,14.241-63.649-8.5-86.485c0,0-32.464,38.778-43.287,51.706C64.65,86.146,31.849,125.327,31.849,125.327z"/>
</g>
</g>
<g>
<image overflow="visible" opacity="0.3" width="354" height="324" xlink:href="data:image/png;base64,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
rkJggg==" transform="matrix(0.24 0 0 0.24 28.4971 38.3643)">
</image>
<g>
<path fill="#FFFFFF" d="M73.043,52.8c0,0-6.48-14.187-19.968-10.958c-13.486,3.23-41.652,30.792,17.853,65.083H70.88
c59.504-34.291,31.34-61.852,17.853-65.083C75.245,38.613,68.766,52.8,68.766,52.8H73.043z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 72 KiB

View file

@ -0,0 +1,10 @@
[Desktop Entry]
Name=LÖVE
Comment=The unquestionably awesome 2D game engine
MimeType=application/x-love-game;
Exec=/home/runner/work/love-appimage-source/love-appimage-source/installdir/bin/love %f
Type=Application
Categories=Development;Game;
Terminal=false
Icon=love
NoDisplay=true

View file

@ -0,0 +1,793 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="141.73px" height="141.73px" viewBox="0 0 141.73 141.73" enable-background="new 0 0 141.73 141.73" xml:space="preserve">
<path fill="#E74A99" d="M110.456,34.395C94.143,17.801,75.931,1.834,70.867,1.834c-7.744,0-68.033,60.968-68.033,68.031
c0,3.348,15.677,21.163,32.088,37.719c1.32-0.855,2.613-1.82,3.864-2.911C63.332,81.653,92.103,62.389,110.456,34.395z"/>
<g>
<g opacity="0.3">
<path d="M112.456,37.395c-18.354,27.994-47.125,47.258-71.67,70.278c-1.251,1.091-2.544,2.056-3.864,2.911
c15.575,15.732,31.811,30.313,35.945,30.313c9.35,0,68.029-61.892,68.029-68.031C140.896,68.501,127.492,52.679,112.456,37.395z"
/>
</g>
<g>
<path fill="#27AAE1" d="M110.456,34.395c-18.354,27.994-47.125,47.258-71.67,70.278c-1.251,1.091-2.544,2.056-3.864,2.911
c15.575,15.732,31.811,30.313,35.946,30.313c9.349,0,68.029-61.892,68.029-68.032C138.896,65.501,125.492,49.679,110.456,34.395z"
/>
</g>
</g>
<g>
<image overflow="visible" opacity="0.3" width="331" height="303" xlink:href="data:image/png;base64,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
RK5CYII=" transform="matrix(0.24 0 0 0.24 31.3364 40.5913)">
</image>
<g>
<path fill="#FFFFFF" d="M72.998,54.174c0,0-5.988-13.127-18.474-10.141c-12.477,2.991-38.536,28.483,16.517,60.218h-0.043
c55.057-31.734,28.998-57.227,16.525-60.218c-12.483-2.986-18.476,10.141-18.476,10.141H72.998z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 59 KiB

View file

@ -0,0 +1,189 @@
----------------------------------------------------------------------------
-- LuaJIT bytecode listing module.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module lists the bytecode of a Lua function. If it's loaded by -jbc
-- it hooks into the parser and lists all functions of a chunk as they
-- are parsed.
--
-- Example usage:
--
-- luajit -jbc -e 'local x=0; for i=1,1e6 do x=x+i end; print(x)'
-- luajit -jbc=- foo.lua
-- luajit -jbc=foo.list foo.lua
--
-- Default output is to stderr. To redirect the output to a file, pass a
-- filename as an argument (use '-' for stdout) or set the environment
-- variable LUAJIT_LISTFILE. The file is overwritten every time the module
-- is started.
--
-- This module can also be used programmatically:
--
-- local bc = require("jit.bc")
--
-- local function foo() print("hello") end
--
-- bc.dump(foo) --> -- BYTECODE -- [...]
-- print(bc.line(foo, 2)) --> 0002 KSTR 1 1 ; "hello"
--
-- local out = {
-- -- Do something with each line:
-- write = function(t, ...) io.write(...) end,
-- close = function(t) end,
-- flush = function(t) end,
-- }
-- bc.dump(foo, out)
--
------------------------------------------------------------------------------
-- Cache some library functions and objects.
local jit = require("jit")
local jutil = require("jit.util")
local vmdef = require("jit.vmdef")
local bit = require("bit")
local sub, gsub, format = string.sub, string.gsub, string.format
local byte, band, shr = string.byte, bit.band, bit.rshift
local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck
local funcuvname = jutil.funcuvname
local bcnames = vmdef.bcnames
local stdout, stderr = io.stdout, io.stderr
------------------------------------------------------------------------------
local function ctlsub(c)
if c == "\n" then return "\\n"
elseif c == "\r" then return "\\r"
elseif c == "\t" then return "\\t"
else return format("\\%03d", byte(c))
end
end
-- Return one bytecode line.
local function bcline(func, pc, prefix)
local ins, m = funcbc(func, pc)
if not ins then return end
local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128)
local a = band(shr(ins, 8), 0xff)
local oidx = 6*band(ins, 0xff)
local op = sub(bcnames, oidx+1, oidx+6)
local s = format("%04d %s %-6s %3s ",
pc, prefix or " ", op, ma == 0 and "" or a)
local d = shr(ins, 16)
if mc == 13*128 then -- BCMjump
return format("%s=> %04d\n", s, pc+d-0x7fff)
end
if mb ~= 0 then
d = band(d, 0xff)
elseif mc == 0 then
return s.."\n"
end
local kc
if mc == 10*128 then -- BCMstr
kc = funck(func, -d-1)
kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub))
elseif mc == 9*128 then -- BCMnum
kc = funck(func, d)
if op == "TSETM " then kc = kc - 2^52 end
elseif mc == 12*128 then -- BCMfunc
local fi = funcinfo(funck(func, -d-1))
if fi.ffid then
kc = vmdef.ffnames[fi.ffid]
else
kc = fi.loc
end
elseif mc == 5*128 then -- BCMuv
kc = funcuvname(func, d)
end
if ma == 5 then -- BCMuv
local ka = funcuvname(func, a)
if kc then kc = ka.." ; "..kc else kc = ka end
end
if mb ~= 0 then
local b = shr(ins, 24)
if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end
return format("%s%3d %3d\n", s, b, d)
end
if kc then return format("%s%3d ; %s\n", s, d, kc) end
if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits
return format("%s%3d\n", s, d)
end
-- Collect branch targets of a function.
local function bctargets(func)
local target = {}
for pc=1,1000000000 do
local ins, m = funcbc(func, pc)
if not ins then break end
if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end
end
return target
end
-- Dump bytecode instructions of a function.
local function bcdump(func, out, all)
if not out then out = stdout end
local fi = funcinfo(func)
if all and fi.children then
for n=-1,-1000000000,-1 do
local k = funck(func, n)
if not k then break end
if type(k) == "proto" then bcdump(k, out, true) end
end
end
out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined))
local target = bctargets(func)
for pc=1,1000000000 do
local s = bcline(func, pc, target[pc] and "=>")
if not s then break end
out:write(s)
end
out:write("\n")
out:flush()
end
------------------------------------------------------------------------------
-- Active flag and output file handle.
local active, out
-- List handler.
local function h_list(func)
return bcdump(func, out)
end
-- Detach list handler.
local function bclistoff()
if active then
active = false
jit.attach(h_list)
if out and out ~= stdout and out ~= stderr then out:close() end
out = nil
end
end
-- Open the output file and attach list handler.
local function bcliston(outfile)
if active then bclistoff() end
if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end
if outfile then
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
else
out = stderr
end
jit.attach(h_list, "bc")
active = true
end
-- Public module functions.
return {
line = bcline,
dump = bcdump,
targets = bctargets,
on = bcliston,
off = bclistoff,
start = bcliston -- For -j command line option.
}

View file

@ -0,0 +1,705 @@
----------------------------------------------------------------------------
-- LuaJIT module to save/list bytecode.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module saves or lists the bytecode for an input file.
-- It's run by the -b command line option.
--
------------------------------------------------------------------------------
local jit = require("jit")
assert(jit.version_num == 20199, "LuaJIT core/library version mismatch")
local bit = require("bit")
-- Symbol name prefix for LuaJIT bytecode.
local LJBC_PREFIX = "luaJIT_BC_"
local type, assert = type, assert
local format = string.format
local tremove, tconcat = table.remove, table.concat
------------------------------------------------------------------------------
local function usage()
io.stderr:write[[
Save LuaJIT bytecode: luajit -b[options] input output
-l Only list bytecode.
-s Strip debug info (default).
-g Keep debug info.
-n name Set module name (default: auto-detect from input name).
-t type Set output file type (default: auto-detect from output name).
-a arch Override architecture for object files (default: native).
-o os Override OS for object files (default: native).
-F name Override filename (default: input filename).
-e chunk Use chunk string as input.
-- Stop handling options.
- Use stdin as input and/or stdout as output.
File types: c cc h obj o raw (default)
]]
os.exit(1)
end
local function check(ok, ...)
if ok then return ok, ... end
io.stderr:write("luajit: ", ...)
io.stderr:write("\n")
os.exit(1)
end
local function readfile(ctx, input)
if type(input) == "function" then return input end
if ctx.filename then
local data
if input == "-" then
data = io.stdin:read("*a")
else
local fp = assert(io.open(input, "rb"))
data = assert(fp:read("*a"))
assert(fp:close())
end
return check(load(data, ctx.filename))
else
if input == "-" then input = nil end
return check(loadfile(input))
end
end
local function savefile(name, mode)
if name == "-" then return io.stdout end
return check(io.open(name, mode))
end
local function set_stdout_binary(ffi)
ffi.cdef[[int _setmode(int fd, int mode);]]
ffi.C._setmode(1, 0x8000)
end
------------------------------------------------------------------------------
local map_type = {
raw = "raw", c = "c", cc = "c", h = "h", o = "obj", obj = "obj",
}
local map_arch = {
x86 = { e = "le", b = 32, m = 3, p = 0x14c, },
x64 = { e = "le", b = 64, m = 62, p = 0x8664, },
arm = { e = "le", b = 32, m = 40, p = 0x1c0, },
arm64 = { e = "le", b = 64, m = 183, p = 0xaa64, },
arm64be = { e = "be", b = 64, m = 183, },
ppc = { e = "be", b = 32, m = 20, },
mips = { e = "be", b = 32, m = 8, f = 0x50001006, },
mipsel = { e = "le", b = 32, m = 8, f = 0x50001006, },
mips64 = { e = "be", b = 64, m = 8, f = 0x80000007, },
mips64el = { e = "le", b = 64, m = 8, f = 0x80000007, },
mips64r6 = { e = "be", b = 64, m = 8, f = 0xa0000407, },
mips64r6el = { e = "le", b = 64, m = 8, f = 0xa0000407, },
}
local map_os = {
linux = true, windows = true, osx = true, freebsd = true, netbsd = true,
openbsd = true, dragonfly = true, solaris = true,
}
local function checkarg(str, map, err)
str = str:lower()
local s = check(map[str], "unknown ", err)
return type(s) == "string" and s or str
end
local function detecttype(str)
local ext = str:lower():match("%.(%a+)$")
return map_type[ext] or "raw"
end
local function checkmodname(str)
check(str:match("^[%w_.%-]+$"), "bad module name")
return str:gsub("[%.%-]", "_")
end
local function detectmodname(str)
if type(str) == "string" then
local tail = str:match("[^/\\]+$")
if tail then str = tail end
local head = str:match("^(.*)%.[^.]*$")
if head then str = head end
str = str:match("^[%w_.%-]+")
else
str = nil
end
check(str, "cannot derive module name, use -n name")
return str:gsub("[%.%-]", "_")
end
------------------------------------------------------------------------------
local function bcsave_tail(fp, output, s)
local ok, err = fp:write(s)
if ok and output ~= "-" then ok, err = fp:close() end
check(ok, "cannot write ", output, ": ", err)
end
local function bcsave_raw(output, s)
if output == "-" and jit.os == "Windows" then
local ok, ffi = pcall(require, "ffi")
check(ok, "FFI library required to write binary file to stdout")
set_stdout_binary(ffi)
end
local fp = savefile(output, "wb")
bcsave_tail(fp, output, s)
end
local function bcsave_c(ctx, output, s)
local fp = savefile(output, "w")
if ctx.type == "c" then
fp:write(format([[
#ifdef __cplusplus
extern "C"
#endif
#ifdef _WIN32
__declspec(dllexport)
#endif
const unsigned char %s%s[] = {
]], LJBC_PREFIX, ctx.modname))
else
fp:write(format([[
#define %s%s_SIZE %d
static const unsigned char %s%s[] = {
]], LJBC_PREFIX, ctx.modname, #s, LJBC_PREFIX, ctx.modname))
end
local t, n, m = {}, 0, 0
for i=1,#s do
local b = tostring(string.byte(s, i))
m = m + #b + 1
if m > 78 then
fp:write(tconcat(t, ",", 1, n), ",\n")
n, m = 0, #b + 1
end
n = n + 1
t[n] = b
end
bcsave_tail(fp, output, tconcat(t, ",", 1, n).."\n};\n")
end
local function bcsave_elfobj(ctx, output, s, ffi)
ffi.cdef[[
typedef struct {
uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
uint16_t type, machine;
uint32_t version;
uint32_t entry, phofs, shofs;
uint32_t flags;
uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
} ELF32header;
typedef struct {
uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
uint16_t type, machine;
uint32_t version;
uint64_t entry, phofs, shofs;
uint32_t flags;
uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
} ELF64header;
typedef struct {
uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize;
} ELF32sectheader;
typedef struct {
uint32_t name, type;
uint64_t flags, addr, ofs, size;
uint32_t link, info;
uint64_t align, entsize;
} ELF64sectheader;
typedef struct {
uint32_t name, value, size;
uint8_t info, other;
uint16_t sectidx;
} ELF32symbol;
typedef struct {
uint32_t name;
uint8_t info, other;
uint16_t sectidx;
uint64_t value, size;
} ELF64symbol;
typedef struct {
ELF32header hdr;
ELF32sectheader sect[6];
ELF32symbol sym[2];
uint8_t space[4096];
} ELF32obj;
typedef struct {
ELF64header hdr;
ELF64sectheader sect[6];
ELF64symbol sym[2];
uint8_t space[4096];
} ELF64obj;
]]
local symname = LJBC_PREFIX..ctx.modname
local ai = assert(map_arch[ctx.arch])
local is64, isbe = ai.b == 64, ai.e == "be"
-- Handle different host/target endianess.
local function f32(x) return x end
local f16, fofs = f32, f32
if ffi.abi("be") ~= isbe then
f32 = bit.bswap
function f16(x) return bit.rshift(bit.bswap(x), 16) end
if is64 then
local two32 = ffi.cast("int64_t", 2^32)
function fofs(x) return bit.bswap(x)*two32 end
else
fofs = f32
end
end
-- Create ELF object and fill in header.
local o = ffi.new(is64 and "ELF64obj" or "ELF32obj")
local hdr = o.hdr
if ctx.os == "bsd" or ctx.os == "other" then -- Determine native hdr.eosabi.
local bf = assert(io.open("/bin/ls", "rb"))
local bs = bf:read(9)
bf:close()
ffi.copy(o, bs, 9)
check(hdr.emagic[0] == 127, "no support for writing native object files")
else
hdr.emagic = "\127ELF"
hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0
end
hdr.eclass = is64 and 2 or 1
hdr.eendian = isbe and 2 or 1
hdr.eversion = 1
hdr.type = f16(1)
hdr.machine = f16(ai.m)
hdr.flags = f32(ai.f or 0)
hdr.version = f32(1)
hdr.shofs = fofs(ffi.offsetof(o, "sect"))
hdr.ehsize = f16(ffi.sizeof(hdr))
hdr.shentsize = f16(ffi.sizeof(o.sect[0]))
hdr.shnum = f16(6)
hdr.shstridx = f16(2)
-- Fill in sections and symbols.
local sofs, ofs = ffi.offsetof(o, "space"), 1
for i,name in ipairs{
".symtab", ".shstrtab", ".strtab", ".rodata", ".note.GNU-stack",
} do
local sect = o.sect[i]
sect.align = fofs(1)
sect.name = f32(ofs)
ffi.copy(o.space+ofs, name)
ofs = ofs + #name+1
end
o.sect[1].type = f32(2) -- .symtab
o.sect[1].link = f32(3)
o.sect[1].info = f32(1)
o.sect[1].align = fofs(8)
o.sect[1].ofs = fofs(ffi.offsetof(o, "sym"))
o.sect[1].entsize = fofs(ffi.sizeof(o.sym[0]))
o.sect[1].size = fofs(ffi.sizeof(o.sym))
o.sym[1].name = f32(1)
o.sym[1].sectidx = f16(4)
o.sym[1].size = fofs(#s)
o.sym[1].info = 17
o.sect[2].type = f32(3) -- .shstrtab
o.sect[2].ofs = fofs(sofs)
o.sect[2].size = fofs(ofs)
o.sect[3].type = f32(3) -- .strtab
o.sect[3].ofs = fofs(sofs + ofs)
o.sect[3].size = fofs(#symname+2)
ffi.copy(o.space+ofs+1, symname)
ofs = ofs + #symname + 2
o.sect[4].type = f32(1) -- .rodata
o.sect[4].flags = fofs(2)
o.sect[4].ofs = fofs(sofs + ofs)
o.sect[4].size = fofs(#s)
o.sect[5].type = f32(1) -- .note.GNU-stack
o.sect[5].ofs = fofs(sofs + ofs + #s)
-- Write ELF object file.
local fp = savefile(output, "wb")
fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
bcsave_tail(fp, output, s)
end
local function bcsave_peobj(ctx, output, s, ffi)
ffi.cdef[[
typedef struct {
uint16_t arch, nsects;
uint32_t time, symtabofs, nsyms;
uint16_t opthdrsz, flags;
} PEheader;
typedef struct {
char name[8];
uint32_t vsize, vaddr, size, ofs, relocofs, lineofs;
uint16_t nreloc, nline;
uint32_t flags;
} PEsection;
typedef struct __attribute((packed)) {
union {
char name[8];
uint32_t nameref[2];
};
uint32_t value;
int16_t sect;
uint16_t type;
uint8_t scl, naux;
} PEsym;
typedef struct __attribute((packed)) {
uint32_t size;
uint16_t nreloc, nline;
uint32_t cksum;
uint16_t assoc;
uint8_t comdatsel, unused[3];
} PEsymaux;
typedef struct {
PEheader hdr;
PEsection sect[2];
// Must be an even number of symbol structs.
PEsym sym0;
PEsymaux sym0aux;
PEsym sym1;
PEsymaux sym1aux;
PEsym sym2;
PEsym sym3;
uint32_t strtabsize;
uint8_t space[4096];
} PEobj;
]]
local symname = LJBC_PREFIX..ctx.modname
local ai = assert(map_arch[ctx.arch])
local is64 = ai.b == 64
local symexport = " /EXPORT:"..symname..",DATA "
-- The file format is always little-endian. Swap if the host is big-endian.
local function f32(x) return x end
local f16 = f32
if ffi.abi("be") then
f32 = bit.bswap
function f16(x) return bit.rshift(bit.bswap(x), 16) end
end
-- Create PE object and fill in header.
local o = ffi.new("PEobj")
local hdr = o.hdr
hdr.arch = f16(assert(ai.p))
hdr.nsects = f16(2)
hdr.symtabofs = f32(ffi.offsetof(o, "sym0"))
hdr.nsyms = f32(6)
-- Fill in sections and symbols.
o.sect[0].name = ".drectve"
o.sect[0].size = f32(#symexport)
o.sect[0].flags = f32(0x00100a00)
o.sym0.sect = f16(1)
o.sym0.scl = 3
o.sym0.name = ".drectve"
o.sym0.naux = 1
o.sym0aux.size = f32(#symexport)
o.sect[1].name = ".rdata"
o.sect[1].size = f32(#s)
o.sect[1].flags = f32(0x40300040)
o.sym1.sect = f16(2)
o.sym1.scl = 3
o.sym1.name = ".rdata"
o.sym1.naux = 1
o.sym1aux.size = f32(#s)
o.sym2.sect = f16(2)
o.sym2.scl = 2
o.sym2.nameref[1] = f32(4)
o.sym3.sect = f16(-1)
o.sym3.scl = 2
o.sym3.value = f32(1)
o.sym3.name = "@feat.00" -- Mark as SafeSEH compliant.
ffi.copy(o.space, symname)
local ofs = #symname + 1
o.strtabsize = f32(ofs + 4)
o.sect[0].ofs = f32(ffi.offsetof(o, "space") + ofs)
ffi.copy(o.space + ofs, symexport)
ofs = ofs + #symexport
o.sect[1].ofs = f32(ffi.offsetof(o, "space") + ofs)
-- Write PE object file.
local fp = savefile(output, "wb")
fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
bcsave_tail(fp, output, s)
end
local function bcsave_machobj(ctx, output, s, ffi)
ffi.cdef[[
typedef struct
{
uint32_t magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags;
} mach_header;
typedef struct
{
mach_header; uint32_t reserved;
} mach_header_64;
typedef struct {
uint32_t cmd, cmdsize;
char segname[16];
uint32_t vmaddr, vmsize, fileoff, filesize;
uint32_t maxprot, initprot, nsects, flags;
} mach_segment_command;
typedef struct {
uint32_t cmd, cmdsize;
char segname[16];
uint64_t vmaddr, vmsize, fileoff, filesize;
uint32_t maxprot, initprot, nsects, flags;
} mach_segment_command_64;
typedef struct {
char sectname[16], segname[16];
uint32_t addr, size;
uint32_t offset, align, reloff, nreloc, flags;
uint32_t reserved1, reserved2;
} mach_section;
typedef struct {
char sectname[16], segname[16];
uint64_t addr, size;
uint32_t offset, align, reloff, nreloc, flags;
uint32_t reserved1, reserved2, reserved3;
} mach_section_64;
typedef struct {
uint32_t cmd, cmdsize, symoff, nsyms, stroff, strsize;
} mach_symtab_command;
typedef struct {
int32_t strx;
uint8_t type, sect;
int16_t desc;
uint32_t value;
} mach_nlist;
typedef struct {
int32_t strx;
uint8_t type, sect;
uint16_t desc;
uint64_t value;
} mach_nlist_64;
typedef struct
{
int32_t magic, nfat_arch;
} mach_fat_header;
typedef struct
{
int32_t cputype, cpusubtype, offset, size, align;
} mach_fat_arch;
typedef struct {
struct {
mach_header hdr;
mach_segment_command seg;
mach_section sec;
mach_symtab_command sym;
} arch[1];
mach_nlist sym_entry;
uint8_t space[4096];
} mach_obj;
typedef struct {
struct {
mach_header_64 hdr;
mach_segment_command_64 seg;
mach_section_64 sec;
mach_symtab_command sym;
} arch[1];
mach_nlist_64 sym_entry;
uint8_t space[4096];
} mach_obj_64;
typedef struct {
mach_fat_header fat;
mach_fat_arch fat_arch[2];
struct {
mach_header hdr;
mach_segment_command seg;
mach_section sec;
mach_symtab_command sym;
} arch[2];
mach_nlist sym_entry;
uint8_t space[4096];
} mach_fat_obj;
typedef struct {
mach_fat_header fat;
mach_fat_arch fat_arch[2];
struct {
mach_header_64 hdr;
mach_segment_command_64 seg;
mach_section_64 sec;
mach_symtab_command sym;
} arch[2];
mach_nlist_64 sym_entry;
uint8_t space[4096];
} mach_fat_obj_64;
]]
local symname = '_'..LJBC_PREFIX..ctx.modname
local isfat, is64, align, mobj = false, false, 4, "mach_obj"
if ctx.arch == "x64" then
is64, align, mobj = true, 8, "mach_obj_64"
elseif ctx.arch == "arm" then
isfat, mobj = true, "mach_fat_obj"
elseif ctx.arch == "arm64" then
is64, align, isfat, mobj = true, 8, true, "mach_fat_obj_64"
else
check(ctx.arch == "x86", "unsupported architecture for OSX")
end
local function aligned(v, a) return bit.band(v+a-1, -a) end
local be32 = bit.bswap -- Mach-O FAT is BE, supported archs are LE.
-- Create Mach-O object and fill in header.
local o = ffi.new(mobj)
local mach_size = aligned(ffi.offsetof(o, "space")+#symname+2, align)
local cputype = ({ x86={7}, x64={0x01000007}, arm={7,12}, arm64={0x01000007,0x0100000c} })[ctx.arch]
local cpusubtype = ({ x86={3}, x64={3}, arm={3,9}, arm64={3,0} })[ctx.arch]
if isfat then
o.fat.magic = be32(0xcafebabe)
o.fat.nfat_arch = be32(#cpusubtype)
end
-- Fill in sections and symbols.
for i=0,#cpusubtype-1 do
local ofs = 0
if isfat then
local a = o.fat_arch[i]
a.cputype = be32(cputype[i+1])
a.cpusubtype = be32(cpusubtype[i+1])
-- Subsequent slices overlap each other to share data.
ofs = ffi.offsetof(o, "arch") + i*ffi.sizeof(o.arch[0])
a.offset = be32(ofs)
a.size = be32(mach_size-ofs+#s)
end
local a = o.arch[i]
a.hdr.magic = is64 and 0xfeedfacf or 0xfeedface
a.hdr.cputype = cputype[i+1]
a.hdr.cpusubtype = cpusubtype[i+1]
a.hdr.filetype = 1
a.hdr.ncmds = 2
a.hdr.sizeofcmds = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)+ffi.sizeof(a.sym)
a.seg.cmd = is64 and 0x19 or 0x1
a.seg.cmdsize = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)
a.seg.vmsize = #s
a.seg.fileoff = mach_size-ofs
a.seg.filesize = #s
a.seg.maxprot = 1
a.seg.initprot = 1
a.seg.nsects = 1
ffi.copy(a.sec.sectname, "__data")
ffi.copy(a.sec.segname, "__DATA")
a.sec.size = #s
a.sec.offset = mach_size-ofs
a.sym.cmd = 2
a.sym.cmdsize = ffi.sizeof(a.sym)
a.sym.symoff = ffi.offsetof(o, "sym_entry")-ofs
a.sym.nsyms = 1
a.sym.stroff = ffi.offsetof(o, "sym_entry")+ffi.sizeof(o.sym_entry)-ofs
a.sym.strsize = aligned(#symname+2, align)
end
o.sym_entry.type = 0xf
o.sym_entry.sect = 1
o.sym_entry.strx = 1
ffi.copy(o.space+1, symname)
-- Write Macho-O object file.
local fp = savefile(output, "wb")
fp:write(ffi.string(o, mach_size))
bcsave_tail(fp, output, s)
end
local function bcsave_obj(ctx, output, s)
local ok, ffi = pcall(require, "ffi")
check(ok, "FFI library required to write this file type")
if output == "-" and jit.os == "Windows" then
set_stdout_binary(ffi)
end
if ctx.os == "windows" then
return bcsave_peobj(ctx, output, s, ffi)
elseif ctx.os == "osx" then
return bcsave_machobj(ctx, output, s, ffi)
else
return bcsave_elfobj(ctx, output, s, ffi)
end
end
------------------------------------------------------------------------------
local function bclist(ctx, input, output)
local f = readfile(ctx, input)
require("jit.bc").dump(f, savefile(output, "w"), true)
end
local function bcsave(ctx, input, output)
local f = readfile(ctx, input)
local s = string.dump(f, ctx.strip)
local t = ctx.type
if not t then
t = detecttype(output)
ctx.type = t
end
if t == "raw" then
bcsave_raw(output, s)
else
if not ctx.modname then ctx.modname = detectmodname(input) end
if t == "obj" then
bcsave_obj(ctx, output, s)
else
bcsave_c(ctx, output, s)
end
end
end
local function docmd(...)
local arg = {...}
local n = 1
local list = false
local ctx = {
strip = true, arch = jit.arch, os = jit.os:lower(),
type = false, modname = false,
}
while n <= #arg do
local a = arg[n]
if type(a) == "string" and a:sub(1, 1) == "-" and a ~= "-" then
tremove(arg, n)
if a == "--" then break end
for m=2,#a do
local opt = a:sub(m, m)
if opt == "l" then
list = true
elseif opt == "s" then
ctx.strip = true
elseif opt == "g" then
ctx.strip = false
else
if arg[n] == nil or m ~= #a then usage() end
if opt == "e" then
if n ~= 1 then usage() end
arg[1] = check(loadstring(arg[1]))
elseif opt == "n" then
ctx.modname = checkmodname(tremove(arg, n))
elseif opt == "t" then
ctx.type = checkarg(tremove(arg, n), map_type, "file type")
elseif opt == "a" then
ctx.arch = checkarg(tremove(arg, n), map_arch, "architecture")
elseif opt == "o" then
ctx.os = checkarg(tremove(arg, n), map_os, "OS name")
elseif opt == "F" then
ctx.filename = "@"..tremove(arg, n)
else
usage()
end
end
end
else
n = n + 1
end
end
if list then
if #arg == 0 or #arg > 2 then usage() end
bclist(ctx, arg[1], arg[2] or "-")
else
if #arg ~= 2 then usage() end
bcsave(ctx, arg[1], arg[2])
end
end
------------------------------------------------------------------------------
-- Public module functions.
return {
start = docmd -- Process -b command line option.
}

View file

@ -0,0 +1,689 @@
----------------------------------------------------------------------------
-- LuaJIT ARM disassembler module.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This is a helper module used by the LuaJIT machine code dumper module.
--
-- It disassembles most user-mode ARMv7 instructions
-- NYI: Advanced SIMD and VFP instructions.
------------------------------------------------------------------------------
local type = type
local sub, byte, format = string.sub, string.byte, string.format
local match, gmatch = string.match, string.gmatch
local concat = table.concat
local bit = require("bit")
local band, bor, ror, tohex = bit.band, bit.bor, bit.ror, bit.tohex
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
------------------------------------------------------------------------------
-- Opcode maps
------------------------------------------------------------------------------
local map_loadc = {
shift = 8, mask = 15,
[10] = {
shift = 20, mask = 1,
[0] = {
shift = 23, mask = 3,
[0] = "vmovFmDN", "vstmFNdr",
_ = {
shift = 21, mask = 1,
[0] = "vstrFdl",
{ shift = 16, mask = 15, [13] = "vpushFdr", _ = "vstmdbFNdr", }
},
},
{
shift = 23, mask = 3,
[0] = "vmovFDNm",
{ shift = 16, mask = 15, [13] = "vpopFdr", _ = "vldmFNdr", },
_ = {
shift = 21, mask = 1,
[0] = "vldrFdl", "vldmdbFNdr",
},
},
},
[11] = {
shift = 20, mask = 1,
[0] = {
shift = 23, mask = 3,
[0] = "vmovGmDN", "vstmGNdr",
_ = {
shift = 21, mask = 1,
[0] = "vstrGdl",
{ shift = 16, mask = 15, [13] = "vpushGdr", _ = "vstmdbGNdr", }
},
},
{
shift = 23, mask = 3,
[0] = "vmovGDNm",
{ shift = 16, mask = 15, [13] = "vpopGdr", _ = "vldmGNdr", },
_ = {
shift = 21, mask = 1,
[0] = "vldrGdl", "vldmdbGNdr",
},
},
},
_ = {
shift = 0, mask = 0 -- NYI ldc, mcrr, mrrc.
},
}
local map_vfps = {
shift = 6, mask = 0x2c001,
[0] = "vmlaF.dnm", "vmlsF.dnm",
[0x04000] = "vnmlsF.dnm", [0x04001] = "vnmlaF.dnm",
[0x08000] = "vmulF.dnm", [0x08001] = "vnmulF.dnm",
[0x0c000] = "vaddF.dnm", [0x0c001] = "vsubF.dnm",
[0x20000] = "vdivF.dnm",
[0x24000] = "vfnmsF.dnm", [0x24001] = "vfnmaF.dnm",
[0x28000] = "vfmaF.dnm", [0x28001] = "vfmsF.dnm",
[0x2c000] = "vmovF.dY",
[0x2c001] = {
shift = 7, mask = 0x1e01,
[0] = "vmovF.dm", "vabsF.dm",
[0x0200] = "vnegF.dm", [0x0201] = "vsqrtF.dm",
[0x0800] = "vcmpF.dm", [0x0801] = "vcmpeF.dm",
[0x0a00] = "vcmpzF.d", [0x0a01] = "vcmpzeF.d",
[0x0e01] = "vcvtG.dF.m",
[0x1000] = "vcvt.f32.u32Fdm", [0x1001] = "vcvt.f32.s32Fdm",
[0x1800] = "vcvtr.u32F.dm", [0x1801] = "vcvt.u32F.dm",
[0x1a00] = "vcvtr.s32F.dm", [0x1a01] = "vcvt.s32F.dm",
},
}
local map_vfpd = {
shift = 6, mask = 0x2c001,
[0] = "vmlaG.dnm", "vmlsG.dnm",
[0x04000] = "vnmlsG.dnm", [0x04001] = "vnmlaG.dnm",
[0x08000] = "vmulG.dnm", [0x08001] = "vnmulG.dnm",
[0x0c000] = "vaddG.dnm", [0x0c001] = "vsubG.dnm",
[0x20000] = "vdivG.dnm",
[0x24000] = "vfnmsG.dnm", [0x24001] = "vfnmaG.dnm",
[0x28000] = "vfmaG.dnm", [0x28001] = "vfmsG.dnm",
[0x2c000] = "vmovG.dY",
[0x2c001] = {
shift = 7, mask = 0x1e01,
[0] = "vmovG.dm", "vabsG.dm",
[0x0200] = "vnegG.dm", [0x0201] = "vsqrtG.dm",
[0x0800] = "vcmpG.dm", [0x0801] = "vcmpeG.dm",
[0x0a00] = "vcmpzG.d", [0x0a01] = "vcmpzeG.d",
[0x0e01] = "vcvtF.dG.m",
[0x1000] = "vcvt.f64.u32GdFm", [0x1001] = "vcvt.f64.s32GdFm",
[0x1800] = "vcvtr.u32FdG.m", [0x1801] = "vcvt.u32FdG.m",
[0x1a00] = "vcvtr.s32FdG.m", [0x1a01] = "vcvt.s32FdG.m",
},
}
local map_datac = {
shift = 24, mask = 1,
[0] = {
shift = 4, mask = 1,
[0] = {
shift = 8, mask = 15,
[10] = map_vfps,
[11] = map_vfpd,
-- NYI cdp, mcr, mrc.
},
{
shift = 8, mask = 15,
[10] = {
shift = 20, mask = 15,
[0] = "vmovFnD", "vmovFDn",
[14] = "vmsrD",
[15] = { shift = 12, mask = 15, [15] = "vmrs", _ = "vmrsD", },
},
},
},
"svcT",
}
local map_loadcu = {
shift = 0, mask = 0, -- NYI unconditional CP load/store.
}
local map_datacu = {
shift = 0, mask = 0, -- NYI unconditional CP data.
}
local map_simddata = {
shift = 0, mask = 0, -- NYI SIMD data.
}
local map_simdload = {
shift = 0, mask = 0, -- NYI SIMD load/store, preload.
}
local map_preload = {
shift = 0, mask = 0, -- NYI preload.
}
local map_media = {
shift = 20, mask = 31,
[0] = false,
{ --01
shift = 5, mask = 7,
[0] = "sadd16DNM", "sasxDNM", "ssaxDNM", "ssub16DNM",
"sadd8DNM", false, false, "ssub8DNM",
},
{ --02
shift = 5, mask = 7,
[0] = "qadd16DNM", "qasxDNM", "qsaxDNM", "qsub16DNM",
"qadd8DNM", false, false, "qsub8DNM",
},
{ --03
shift = 5, mask = 7,
[0] = "shadd16DNM", "shasxDNM", "shsaxDNM", "shsub16DNM",
"shadd8DNM", false, false, "shsub8DNM",
},
false,
{ --05
shift = 5, mask = 7,
[0] = "uadd16DNM", "uasxDNM", "usaxDNM", "usub16DNM",
"uadd8DNM", false, false, "usub8DNM",
},
{ --06
shift = 5, mask = 7,
[0] = "uqadd16DNM", "uqasxDNM", "uqsaxDNM", "uqsub16DNM",
"uqadd8DNM", false, false, "uqsub8DNM",
},
{ --07
shift = 5, mask = 7,
[0] = "uhadd16DNM", "uhasxDNM", "uhsaxDNM", "uhsub16DNM",
"uhadd8DNM", false, false, "uhsub8DNM",
},
{ --08
shift = 5, mask = 7,
[0] = "pkhbtDNMU", false, "pkhtbDNMU",
{ shift = 16, mask = 15, [15] = "sxtb16DMU", _ = "sxtab16DNMU", },
"pkhbtDNMU", "selDNM", "pkhtbDNMU",
},
false,
{ --0a
shift = 5, mask = 7,
[0] = "ssatDxMu", "ssat16DxM", "ssatDxMu",
{ shift = 16, mask = 15, [15] = "sxtbDMU", _ = "sxtabDNMU", },
"ssatDxMu", false, "ssatDxMu",
},
{ --0b
shift = 5, mask = 7,
[0] = "ssatDxMu", "revDM", "ssatDxMu",
{ shift = 16, mask = 15, [15] = "sxthDMU", _ = "sxtahDNMU", },
"ssatDxMu", "rev16DM", "ssatDxMu",
},
{ --0c
shift = 5, mask = 7,
[3] = { shift = 16, mask = 15, [15] = "uxtb16DMU", _ = "uxtab16DNMU", },
},
false,
{ --0e
shift = 5, mask = 7,
[0] = "usatDwMu", "usat16DwM", "usatDwMu",
{ shift = 16, mask = 15, [15] = "uxtbDMU", _ = "uxtabDNMU", },
"usatDwMu", false, "usatDwMu",
},
{ --0f
shift = 5, mask = 7,
[0] = "usatDwMu", "rbitDM", "usatDwMu",
{ shift = 16, mask = 15, [15] = "uxthDMU", _ = "uxtahDNMU", },
"usatDwMu", "revshDM", "usatDwMu",
},
{ --10
shift = 12, mask = 15,
[15] = {
shift = 5, mask = 7,
"smuadNMS", "smuadxNMS", "smusdNMS", "smusdxNMS",
},
_ = {
shift = 5, mask = 7,
[0] = "smladNMSD", "smladxNMSD", "smlsdNMSD", "smlsdxNMSD",
},
},
false, false, false,
{ --14
shift = 5, mask = 7,
[0] = "smlaldDNMS", "smlaldxDNMS", "smlsldDNMS", "smlsldxDNMS",
},
{ --15
shift = 5, mask = 7,
[0] = { shift = 12, mask = 15, [15] = "smmulNMS", _ = "smmlaNMSD", },
{ shift = 12, mask = 15, [15] = "smmulrNMS", _ = "smmlarNMSD", },
false, false, false, false,
"smmlsNMSD", "smmlsrNMSD",
},
false, false,
{ --18
shift = 5, mask = 7,
[0] = { shift = 12, mask = 15, [15] = "usad8NMS", _ = "usada8NMSD", },
},
false,
{ --1a
shift = 5, mask = 3, [2] = "sbfxDMvw",
},
{ --1b
shift = 5, mask = 3, [2] = "sbfxDMvw",
},
{ --1c
shift = 5, mask = 3,
[0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
},
{ --1d
shift = 5, mask = 3,
[0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
},
{ --1e
shift = 5, mask = 3, [2] = "ubfxDMvw",
},
{ --1f
shift = 5, mask = 3, [2] = "ubfxDMvw",
},
}
local map_load = {
shift = 21, mask = 9,
{
shift = 20, mask = 5,
[0] = "strtDL", "ldrtDL", [4] = "strbtDL", [5] = "ldrbtDL",
},
_ = {
shift = 20, mask = 5,
[0] = "strDL", "ldrDL", [4] = "strbDL", [5] = "ldrbDL",
}
}
local map_load1 = {
shift = 4, mask = 1,
[0] = map_load, map_media,
}
local map_loadm = {
shift = 20, mask = 1,
[0] = {
shift = 23, mask = 3,
[0] = "stmdaNR", "stmNR",
{ shift = 16, mask = 63, [45] = "pushR", _ = "stmdbNR", }, "stmibNR",
},
{
shift = 23, mask = 3,
[0] = "ldmdaNR", { shift = 16, mask = 63, [61] = "popR", _ = "ldmNR", },
"ldmdbNR", "ldmibNR",
},
}
local map_data = {
shift = 21, mask = 15,
[0] = "andDNPs", "eorDNPs", "subDNPs", "rsbDNPs",
"addDNPs", "adcDNPs", "sbcDNPs", "rscDNPs",
"tstNP", "teqNP", "cmpNP", "cmnNP",
"orrDNPs", "movDPs", "bicDNPs", "mvnDPs",
}
local map_mul = {
shift = 21, mask = 7,
[0] = "mulNMSs", "mlaNMSDs", "umaalDNMS", "mlsDNMS",
"umullDNMSs", "umlalDNMSs", "smullDNMSs", "smlalDNMSs",
}
local map_sync = {
shift = 20, mask = 15, -- NYI: brackets around N. R(D+1) for ldrexd/strexd.
[0] = "swpDMN", false, false, false,
"swpbDMN", false, false, false,
"strexDMN", "ldrexDN", "strexdDN", "ldrexdDN",
"strexbDMN", "ldrexbDN", "strexhDN", "ldrexhDN",
}
local map_mulh = {
shift = 21, mask = 3,
[0] = { shift = 5, mask = 3,
[0] = "smlabbNMSD", "smlatbNMSD", "smlabtNMSD", "smlattNMSD", },
{ shift = 5, mask = 3,
[0] = "smlawbNMSD", "smulwbNMS", "smlawtNMSD", "smulwtNMS", },
{ shift = 5, mask = 3,
[0] = "smlalbbDNMS", "smlaltbDNMS", "smlalbtDNMS", "smlalttDNMS", },
{ shift = 5, mask = 3,
[0] = "smulbbNMS", "smultbNMS", "smulbtNMS", "smulttNMS", },
}
local map_misc = {
shift = 4, mask = 7,
-- NYI: decode PSR bits of msr.
[0] = { shift = 21, mask = 1, [0] = "mrsD", "msrM", },
{ shift = 21, mask = 3, "bxM", false, "clzDM", },
{ shift = 21, mask = 3, "bxjM", },
{ shift = 21, mask = 3, "blxM", },
false,
{ shift = 21, mask = 3, [0] = "qaddDMN", "qsubDMN", "qdaddDMN", "qdsubDMN", },
false,
{ shift = 21, mask = 3, "bkptK", },
}
local map_datar = {
shift = 4, mask = 9,
[9] = {
shift = 5, mask = 3,
[0] = { shift = 24, mask = 1, [0] = map_mul, map_sync, },
{ shift = 20, mask = 1, [0] = "strhDL", "ldrhDL", },
{ shift = 20, mask = 1, [0] = "ldrdDL", "ldrsbDL", },
{ shift = 20, mask = 1, [0] = "strdDL", "ldrshDL", },
},
_ = {
shift = 20, mask = 25,
[16] = { shift = 7, mask = 1, [0] = map_misc, map_mulh, },
_ = {
shift = 0, mask = 0xffffffff,
[bor(0xe1a00000)] = "nop",
_ = map_data,
}
},
}
local map_datai = {
shift = 20, mask = 31, -- NYI: decode PSR bits of msr. Decode imm12.
[16] = "movwDW", [20] = "movtDW",
[18] = { shift = 0, mask = 0xf00ff, [0] = "nopv6", _ = "msrNW", },
[22] = "msrNW",
_ = map_data,
}
local map_branch = {
shift = 24, mask = 1,
[0] = "bB", "blB"
}
local map_condins = {
[0] = map_datar, map_datai, map_load, map_load1,
map_loadm, map_branch, map_loadc, map_datac
}
-- NYI: setend.
local map_uncondins = {
[0] = false, map_simddata, map_simdload, map_preload,
false, "blxB", map_loadcu, map_datacu,
}
------------------------------------------------------------------------------
local map_gpr = {
[0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
}
local map_cond = {
[0] = "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
"hi", "ls", "ge", "lt", "gt", "le", "al",
}
local map_shift = { [0] = "lsl", "lsr", "asr", "ror", }
------------------------------------------------------------------------------
-- Output a nicely formatted line with an opcode and operands.
local function putop(ctx, text, operands)
local pos = ctx.pos
local extra = ""
if ctx.rel then
local sym = ctx.symtab[ctx.rel]
if sym then
extra = "\t->"..sym
elseif band(ctx.op, 0x0e000000) ~= 0x0a000000 then
extra = "\t; 0x"..tohex(ctx.rel)
end
end
if ctx.hexdump > 0 then
ctx.out(format("%08x %s %-5s %s%s\n",
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
else
ctx.out(format("%08x %-5s %s%s\n",
ctx.addr+pos, text, concat(operands, ", "), extra))
end
ctx.pos = pos + 4
end
-- Fallback for unknown opcodes.
local function unknown(ctx)
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
end
-- Format operand 2 of load/store opcodes.
local function fmtload(ctx, op, pos)
local base = map_gpr[band(rshift(op, 16), 15)]
local x, ofs
local ext = (band(op, 0x04000000) == 0)
if not ext and band(op, 0x02000000) == 0 then
ofs = band(op, 4095)
if band(op, 0x00800000) == 0 then ofs = -ofs end
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
ofs = "#"..ofs
elseif ext and band(op, 0x00400000) ~= 0 then
ofs = band(op, 15) + band(rshift(op, 4), 0xf0)
if band(op, 0x00800000) == 0 then ofs = -ofs end
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
ofs = "#"..ofs
else
ofs = map_gpr[band(op, 15)]
if ext or band(op, 0xfe0) == 0 then
elseif band(op, 0xfe0) == 0x60 then
ofs = format("%s, rrx", ofs)
else
local sh = band(rshift(op, 7), 31)
if sh == 0 then sh = 32 end
ofs = format("%s, %s #%d", ofs, map_shift[band(rshift(op, 5), 3)], sh)
end
if band(op, 0x00800000) == 0 then ofs = "-"..ofs end
end
if ofs == "#0" then
x = format("[%s]", base)
elseif band(op, 0x01000000) == 0 then
x = format("[%s], %s", base, ofs)
else
x = format("[%s, %s]", base, ofs)
end
if band(op, 0x01200000) == 0x01200000 then x = x.."!" end
return x
end
-- Format operand 2 of vector load/store opcodes.
local function fmtvload(ctx, op, pos)
local base = map_gpr[band(rshift(op, 16), 15)]
local ofs = band(op, 255)*4
if band(op, 0x00800000) == 0 then ofs = -ofs end
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
if ofs == 0 then
return format("[%s]", base)
else
return format("[%s, #%d]", base, ofs)
end
end
local function fmtvr(op, vr, sh0, sh1)
if vr == "s" then
return format("s%d", 2*band(rshift(op, sh0), 15)+band(rshift(op, sh1), 1))
else
return format("d%d", band(rshift(op, sh0), 15)+band(rshift(op, sh1-4), 16))
end
end
-- Disassemble a single instruction.
local function disass_ins(ctx)
local pos = ctx.pos
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
local operands = {}
local suffix = ""
local last, name, pat
local vr
ctx.op = op
ctx.rel = nil
local cond = rshift(op, 28)
local opat
if cond == 15 then
opat = map_uncondins[band(rshift(op, 25), 7)]
else
if cond ~= 14 then suffix = map_cond[cond] end
opat = map_condins[band(rshift(op, 25), 7)]
end
while type(opat) ~= "string" do
if not opat then return unknown(ctx) end
opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
end
name, pat = match(opat, "^([a-z0-9]*)(.*)")
if sub(pat, 1, 1) == "." then
local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)")
suffix = suffix..s2
pat = p2
end
for p in gmatch(pat, ".") do
local x = nil
if p == "D" then
x = map_gpr[band(rshift(op, 12), 15)]
elseif p == "N" then
x = map_gpr[band(rshift(op, 16), 15)]
elseif p == "S" then
x = map_gpr[band(rshift(op, 8), 15)]
elseif p == "M" then
x = map_gpr[band(op, 15)]
elseif p == "d" then
x = fmtvr(op, vr, 12, 22)
elseif p == "n" then
x = fmtvr(op, vr, 16, 7)
elseif p == "m" then
x = fmtvr(op, vr, 0, 5)
elseif p == "P" then
if band(op, 0x02000000) ~= 0 then
x = ror(band(op, 255), 2*band(rshift(op, 8), 15))
else
x = map_gpr[band(op, 15)]
if band(op, 0xff0) ~= 0 then
operands[#operands+1] = x
local s = map_shift[band(rshift(op, 5), 3)]
local r = nil
if band(op, 0xf90) == 0 then
if s == "ror" then s = "rrx" else r = "#32" end
elseif band(op, 0x10) == 0 then
r = "#"..band(rshift(op, 7), 31)
else
r = map_gpr[band(rshift(op, 8), 15)]
end
if name == "mov" then name = s; x = r
elseif r then x = format("%s %s", s, r)
else x = s end
end
end
elseif p == "L" then
x = fmtload(ctx, op, pos)
elseif p == "l" then
x = fmtvload(ctx, op, pos)
elseif p == "B" then
local addr = ctx.addr + pos + 8 + arshift(lshift(op, 8), 6)
if cond == 15 then addr = addr + band(rshift(op, 23), 2) end
ctx.rel = addr
x = "0x"..tohex(addr)
elseif p == "F" then
vr = "s"
elseif p == "G" then
vr = "d"
elseif p == "." then
suffix = suffix..(vr == "s" and ".f32" or ".f64")
elseif p == "R" then
if band(op, 0x00200000) ~= 0 and #operands == 1 then
operands[1] = operands[1].."!"
end
local t = {}
for i=0,15 do
if band(rshift(op, i), 1) == 1 then t[#t+1] = map_gpr[i] end
end
x = "{"..concat(t, ", ").."}"
elseif p == "r" then
if band(op, 0x00200000) ~= 0 and #operands == 2 then
operands[1] = operands[1].."!"
end
local s = tonumber(sub(last, 2))
local n = band(op, 255)
if vr == "d" then n = rshift(n, 1) end
operands[#operands] = format("{%s-%s%d}", last, vr, s+n-1)
elseif p == "W" then
x = band(op, 0x0fff) + band(rshift(op, 4), 0xf000)
elseif p == "T" then
x = "#0x"..tohex(band(op, 0x00ffffff), 6)
elseif p == "U" then
x = band(rshift(op, 7), 31)
if x == 0 then x = nil end
elseif p == "u" then
x = band(rshift(op, 7), 31)
if band(op, 0x40) == 0 then
if x == 0 then x = nil else x = "lsl #"..x end
else
if x == 0 then x = "asr #32" else x = "asr #"..x end
end
elseif p == "v" then
x = band(rshift(op, 7), 31)
elseif p == "w" then
x = band(rshift(op, 16), 31)
elseif p == "x" then
x = band(rshift(op, 16), 31) + 1
elseif p == "X" then
x = band(rshift(op, 16), 31) - last + 1
elseif p == "Y" then
x = band(rshift(op, 12), 0xf0) + band(op, 0x0f)
elseif p == "K" then
x = "#0x"..tohex(band(rshift(op, 4), 0x0000fff0) + band(op, 15), 4)
elseif p == "s" then
if band(op, 0x00100000) ~= 0 then suffix = "s"..suffix end
else
assert(false)
end
if x then
last = x
if type(x) == "number" then x = "#"..x end
operands[#operands+1] = x
end
end
return putop(ctx, name..suffix, operands)
end
------------------------------------------------------------------------------
-- Disassemble a block of code.
local function disass_block(ctx, ofs, len)
if not ofs then ofs = 0 end
local stop = len and ofs+len or #ctx.code
ctx.pos = ofs
ctx.rel = nil
while ctx.pos < stop do disass_ins(ctx) end
end
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
local function create(code, addr, out)
local ctx = {}
ctx.code = code
ctx.addr = addr or 0
ctx.out = out or io.write
ctx.symtab = {}
ctx.disass = disass_block
ctx.hexdump = 8
return ctx
end
-- Simple API: disassemble code (a string) at address and output via out.
local function disass(code, addr, out)
create(code, addr, out):disass()
end
-- Return register name for RID.
local function regname(r)
if r < 16 then return map_gpr[r] end
return "d"..(r-16)
end
-- Public module functions.
return {
create = create,
disass = disass,
regname = regname
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,12 @@
----------------------------------------------------------------------------
-- LuaJIT ARM64BE disassembler wrapper module.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- ARM64 instructions are always little-endian. So just forward to the
-- common ARM64 disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
return require((string.match(..., ".*%.") or "").."dis_arm64")

View file

@ -0,0 +1,694 @@
----------------------------------------------------------------------------
-- LuaJIT MIPS disassembler module.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT/X license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This is a helper module used by the LuaJIT machine code dumper module.
--
-- It disassembles all standard MIPS32R1/R2 instructions.
-- Default mode is big-endian, but see: dis_mipsel.lua
------------------------------------------------------------------------------
local type = type
local byte, format = string.byte, string.format
local match, gmatch = string.match, string.gmatch
local concat = table.concat
local bit = require("bit")
local band, bor, tohex = bit.band, bit.bor, bit.tohex
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
------------------------------------------------------------------------------
-- Extended opcode maps common to all MIPS releases
------------------------------------------------------------------------------
local map_srl = { shift = 21, mask = 1, [0] = "srlDTA", "rotrDTA", }
local map_srlv = { shift = 6, mask = 1, [0] = "srlvDTS", "rotrvDTS", }
local map_cop0 = {
shift = 25, mask = 1,
[0] = {
shift = 21, mask = 15,
[0] = "mfc0TDW", [4] = "mtc0TDW",
[10] = "rdpgprDT",
[11] = { shift = 5, mask = 1, [0] = "diT0", "eiT0", },
[14] = "wrpgprDT",
}, {
shift = 0, mask = 63,
[1] = "tlbr", [2] = "tlbwi", [6] = "tlbwr", [8] = "tlbp",
[24] = "eret", [31] = "deret",
[32] = "wait",
},
}
------------------------------------------------------------------------------
-- Primary and extended opcode maps for MIPS R1-R5
------------------------------------------------------------------------------
local map_movci = { shift = 16, mask = 1, [0] = "movfDSC", "movtDSC", }
local map_special = {
shift = 0, mask = 63,
[0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
map_movci, map_srl, "sraDTA",
"sllvDTS", false, map_srlv, "sravDTS",
"jrS", "jalrD1S", "movzDST", "movnDST",
"syscallY", "breakY", false, "sync",
"mfhiD", "mthiS", "mfloD", "mtloS",
"dsllvDST", false, "dsrlvDST", "dsravDST",
"multST", "multuST", "divST", "divuST",
"dmultST", "dmultuST", "ddivST", "ddivuST",
"addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
"andDST", "or|moveDST0", "xorDST", "nor|notDST0",
false, false, "sltDST", "sltuDST",
"daddDST", "dadduDST", "dsubDST", "dsubuDST",
"tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
"teqSTZ", false, "tneSTZ", false,
"dsllDTA", false, "dsrlDTA", "dsraDTA",
"dsll32DTA", false, "dsrl32DTA", "dsra32DTA",
}
local map_special2 = {
shift = 0, mask = 63,
[0] = "maddST", "madduST", "mulDST", false,
"msubST", "msubuST",
[32] = "clzDS", [33] = "cloDS",
[63] = "sdbbpY",
}
local map_bshfl = {
shift = 6, mask = 31,
[2] = "wsbhDT",
[16] = "sebDT",
[24] = "sehDT",
}
local map_dbshfl = {
shift = 6, mask = 31,
[2] = "dsbhDT",
[5] = "dshdDT",
}
local map_special3 = {
shift = 0, mask = 63,
[0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK",
[4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL",
[32] = map_bshfl, [36] = map_dbshfl, [59] = "rdhwrTD",
}
local map_regimm = {
shift = 16, mask = 31,
[0] = "bltzSB", "bgezSB", "bltzlSB", "bgezlSB",
false, false, false, false,
"tgeiSI", "tgeiuSI", "tltiSI", "tltiuSI",
"teqiSI", false, "tneiSI", false,
"bltzalSB", "bgezalSB", "bltzallSB", "bgezallSB",
false, false, false, false,
false, false, false, false,
false, false, false, "synciSO",
}
local map_cop1s = {
shift = 0, mask = 63,
[0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH",
"sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG",
"round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG",
"round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG",
false,
{ shift = 16, mask = 1, [0] = "movf.sFGC", "movt.sFGC" },
"movz.sFGT", "movn.sFGT",
false, "recip.sFG", "rsqrt.sFG", false,
false, false, false, false,
false, false, false, false,
false, "cvt.d.sFG", false, false,
"cvt.w.sFG", "cvt.l.sFG", "cvt.ps.sFGH", false,
false, false, false, false,
false, false, false, false,
"c.f.sVGH", "c.un.sVGH", "c.eq.sVGH", "c.ueq.sVGH",
"c.olt.sVGH", "c.ult.sVGH", "c.ole.sVGH", "c.ule.sVGH",
"c.sf.sVGH", "c.ngle.sVGH", "c.seq.sVGH", "c.ngl.sVGH",
"c.lt.sVGH", "c.nge.sVGH", "c.le.sVGH", "c.ngt.sVGH",
}
local map_cop1d = {
shift = 0, mask = 63,
[0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH",
"sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG",
"round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG",
"round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG",
false,
{ shift = 16, mask = 1, [0] = "movf.dFGC", "movt.dFGC" },
"movz.dFGT", "movn.dFGT",
false, "recip.dFG", "rsqrt.dFG", false,
false, false, false, false,
false, false, false, false,
"cvt.s.dFG", false, false, false,
"cvt.w.dFG", "cvt.l.dFG", false, false,
false, false, false, false,
false, false, false, false,
"c.f.dVGH", "c.un.dVGH", "c.eq.dVGH", "c.ueq.dVGH",
"c.olt.dVGH", "c.ult.dVGH", "c.ole.dVGH", "c.ule.dVGH",
"c.df.dVGH", "c.ngle.dVGH", "c.deq.dVGH", "c.ngl.dVGH",
"c.lt.dVGH", "c.nge.dVGH", "c.le.dVGH", "c.ngt.dVGH",
}
local map_cop1ps = {
shift = 0, mask = 63,
[0] = "add.psFGH", "sub.psFGH", "mul.psFGH", false,
false, "abs.psFG", "mov.psFG", "neg.psFG",
false, false, false, false,
false, false, false, false,
false,
{ shift = 16, mask = 1, [0] = "movf.psFGC", "movt.psFGC" },
"movz.psFGT", "movn.psFGT",
false, false, false, false,
false, false, false, false,
false, false, false, false,
"cvt.s.puFG", false, false, false,
false, false, false, false,
"cvt.s.plFG", false, false, false,
"pll.psFGH", "plu.psFGH", "pul.psFGH", "puu.psFGH",
"c.f.psVGH", "c.un.psVGH", "c.eq.psVGH", "c.ueq.psVGH",
"c.olt.psVGH", "c.ult.psVGH", "c.ole.psVGH", "c.ule.psVGH",
"c.psf.psVGH", "c.ngle.psVGH", "c.pseq.psVGH", "c.ngl.psVGH",
"c.lt.psVGH", "c.nge.psVGH", "c.le.psVGH", "c.ngt.psVGH",
}
local map_cop1w = {
shift = 0, mask = 63,
[32] = "cvt.s.wFG", [33] = "cvt.d.wFG",
}
local map_cop1l = {
shift = 0, mask = 63,
[32] = "cvt.s.lFG", [33] = "cvt.d.lFG",
}
local map_cop1bc = {
shift = 16, mask = 3,
[0] = "bc1fCB", "bc1tCB", "bc1flCB", "bc1tlCB",
}
local map_cop1 = {
shift = 21, mask = 31,
[0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG",
"mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG",
map_cop1bc, false, false, false,
false, false, false, false,
map_cop1s, map_cop1d, false, false,
map_cop1w, map_cop1l, map_cop1ps,
}
local map_cop1x = {
shift = 0, mask = 63,
[0] = "lwxc1FSX", "ldxc1FSX", false, false,
false, "luxc1FSX", false, false,
"swxc1FSX", "sdxc1FSX", false, false,
false, "suxc1FSX", false, "prefxMSX",
false, false, false, false,
false, false, false, false,
false, false, false, false,
false, false, "alnv.psFGHS", false,
"madd.sFRGH", "madd.dFRGH", false, false,
false, false, "madd.psFRGH", false,
"msub.sFRGH", "msub.dFRGH", false, false,
false, false, "msub.psFRGH", false,
"nmadd.sFRGH", "nmadd.dFRGH", false, false,
false, false, "nmadd.psFRGH", false,
"nmsub.sFRGH", "nmsub.dFRGH", false, false,
false, false, "nmsub.psFRGH", false,
}
local map_pri = {
[0] = map_special, map_regimm, "jJ", "jalJ",
"beq|beqz|bST00B", "bne|bnezST0B", "blezSB", "bgtzSB",
"addiTSI", "addiu|liTS0I", "sltiTSI", "sltiuTSI",
"andiTSU", "ori|liTS0U", "xoriTSU", "luiTU",
map_cop0, map_cop1, false, map_cop1x,
"beql|beqzlST0B", "bnel|bnezlST0B", "blezlSB", "bgtzlSB",
"daddiTSI", "daddiuTSI", false, false,
map_special2, "jalxJ", false, map_special3,
"lbTSO", "lhTSO", "lwlTSO", "lwTSO",
"lbuTSO", "lhuTSO", "lwrTSO", false,
"sbTSO", "shTSO", "swlTSO", "swTSO",
false, false, "swrTSO", "cacheNSO",
"llTSO", "lwc1HSO", "lwc2TSO", "prefNSO",
false, "ldc1HSO", "ldc2TSO", "ldTSO",
"scTSO", "swc1HSO", "swc2TSO", false,
false, "sdc1HSO", "sdc2TSO", "sdTSO",
}
------------------------------------------------------------------------------
-- Primary and extended opcode maps for MIPS R6
------------------------------------------------------------------------------
local map_mul_r6 = { shift = 6, mask = 3, [2] = "mulDST", [3] = "muhDST" }
local map_mulu_r6 = { shift = 6, mask = 3, [2] = "muluDST", [3] = "muhuDST" }
local map_div_r6 = { shift = 6, mask = 3, [2] = "divDST", [3] = "modDST" }
local map_divu_r6 = { shift = 6, mask = 3, [2] = "divuDST", [3] = "moduDST" }
local map_dmul_r6 = { shift = 6, mask = 3, [2] = "dmulDST", [3] = "dmuhDST" }
local map_dmulu_r6 = { shift = 6, mask = 3, [2] = "dmuluDST", [3] = "dmuhuDST" }
local map_ddiv_r6 = { shift = 6, mask = 3, [2] = "ddivDST", [3] = "dmodDST" }
local map_ddivu_r6 = { shift = 6, mask = 3, [2] = "ddivuDST", [3] = "dmoduDST" }
local map_special_r6 = {
shift = 0, mask = 63,
[0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
false, map_srl, "sraDTA",
"sllvDTS", false, map_srlv, "sravDTS",
"jrS", "jalrD1S", false, false,
"syscallY", "breakY", false, "sync",
"clzDS", "cloDS", "dclzDS", "dcloDS",
"dsllvDST", "dlsaDSTA", "dsrlvDST", "dsravDST",
map_mul_r6, map_mulu_r6, map_div_r6, map_divu_r6,
map_dmul_r6, map_dmulu_r6, map_ddiv_r6, map_ddivu_r6,
"addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
"andDST", "or|moveDST0", "xorDST", "nor|notDST0",
false, false, "sltDST", "sltuDST",
"daddDST", "dadduDST", "dsubDST", "dsubuDST",
"tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
"teqSTZ", "seleqzDST", "tneSTZ", "selnezDST",
"dsllDTA", false, "dsrlDTA", "dsraDTA",
"dsll32DTA", false, "dsrl32DTA", "dsra32DTA",
}
local map_bshfl_r6 = {
shift = 9, mask = 3,
[1] = "alignDSTa",
_ = {
shift = 6, mask = 31,
[0] = "bitswapDT",
[2] = "wsbhDT",
[16] = "sebDT",
[24] = "sehDT",
}
}
local map_dbshfl_r6 = {
shift = 9, mask = 3,
[1] = "dalignDSTa",
_ = {
shift = 6, mask = 31,
[0] = "dbitswapDT",
[2] = "dsbhDT",
[5] = "dshdDT",
}
}
local map_special3_r6 = {
shift = 0, mask = 63,
[0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK",
[4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL",
[32] = map_bshfl_r6, [36] = map_dbshfl_r6, [59] = "rdhwrTD",
}
local map_regimm_r6 = {
shift = 16, mask = 31,
[0] = "bltzSB", [1] = "bgezSB",
[6] = "dahiSI", [30] = "datiSI",
[23] = "sigrieI", [31] = "synciSO",
}
local map_pcrel_r6 = {
shift = 19, mask = 3,
[0] = "addiupcS2", "lwpcS2", "lwupcS2", {
shift = 18, mask = 1,
[0] = "ldpcS3", { shift = 16, mask = 3, [2] = "auipcSI", [3] = "aluipcSI" }
}
}
local map_cop1s_r6 = {
shift = 0, mask = 63,
[0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH",
"sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG",
"round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG",
"round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG",
"sel.sFGH", false, false, false,
"seleqz.sFGH", "recip.sFG", "rsqrt.sFG", "selnez.sFGH",
"maddf.sFGH", "msubf.sFGH", "rint.sFG", "class.sFG",
"min.sFGH", "mina.sFGH", "max.sFGH", "maxa.sFGH",
false, "cvt.d.sFG", false, false,
"cvt.w.sFG", "cvt.l.sFG",
}
local map_cop1d_r6 = {
shift = 0, mask = 63,
[0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH",
"sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG",
"round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG",
"round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG",
"sel.dFGH", false, false, false,
"seleqz.dFGH", "recip.dFG", "rsqrt.dFG", "selnez.dFGH",
"maddf.dFGH", "msubf.dFGH", "rint.dFG", "class.dFG",
"min.dFGH", "mina.dFGH", "max.dFGH", "maxa.dFGH",
"cvt.s.dFG", false, false, false,
"cvt.w.dFG", "cvt.l.dFG",
}
local map_cop1w_r6 = {
shift = 0, mask = 63,
[0] = "cmp.af.sFGH", "cmp.un.sFGH", "cmp.eq.sFGH", "cmp.ueq.sFGH",
"cmp.lt.sFGH", "cmp.ult.sFGH", "cmp.le.sFGH", "cmp.ule.sFGH",
"cmp.saf.sFGH", "cmp.sun.sFGH", "cmp.seq.sFGH", "cmp.sueq.sFGH",
"cmp.slt.sFGH", "cmp.sult.sFGH", "cmp.sle.sFGH", "cmp.sule.sFGH",
false, "cmp.or.sFGH", "cmp.une.sFGH", "cmp.ne.sFGH",
false, false, false, false,
false, "cmp.sor.sFGH", "cmp.sune.sFGH", "cmp.sne.sFGH",
false, false, false, false,
"cvt.s.wFG", "cvt.d.wFG",
}
local map_cop1l_r6 = {
shift = 0, mask = 63,
[0] = "cmp.af.dFGH", "cmp.un.dFGH", "cmp.eq.dFGH", "cmp.ueq.dFGH",
"cmp.lt.dFGH", "cmp.ult.dFGH", "cmp.le.dFGH", "cmp.ule.dFGH",
"cmp.saf.dFGH", "cmp.sun.dFGH", "cmp.seq.dFGH", "cmp.sueq.dFGH",
"cmp.slt.dFGH", "cmp.sult.dFGH", "cmp.sle.dFGH", "cmp.sule.dFGH",
false, "cmp.or.dFGH", "cmp.une.dFGH", "cmp.ne.dFGH",
false, false, false, false,
false, "cmp.sor.dFGH", "cmp.sune.dFGH", "cmp.sne.dFGH",
false, false, false, false,
"cvt.s.lFG", "cvt.d.lFG",
}
local map_cop1_r6 = {
shift = 21, mask = 31,
[0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG",
"mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG",
false, "bc1eqzHB", false, false,
false, "bc1nezHB", false, false,
map_cop1s_r6, map_cop1d_r6, false, false,
map_cop1w_r6, map_cop1l_r6,
}
local function maprs_popTS(rs, rt)
if rt == 0 then return 0 elseif rs == 0 then return 1
elseif rs == rt then return 2 else return 3 end
end
local map_pop06_r6 = {
maprs = maprs_popTS, [0] = "blezSB", "blezalcTB", "bgezalcTB", "bgeucSTB"
}
local map_pop07_r6 = {
maprs = maprs_popTS, [0] = "bgtzSB", "bgtzalcTB", "bltzalcTB", "bltucSTB"
}
local map_pop26_r6 = {
maprs = maprs_popTS, "blezcTB", "bgezcTB", "bgecSTB"
}
local map_pop27_r6 = {
maprs = maprs_popTS, "bgtzcTB", "bltzcTB", "bltcSTB"
}
local function maprs_popS(rs, rt)
if rs == 0 then return 0 else return 1 end
end
local map_pop66_r6 = {
maprs = maprs_popS, [0] = "jicTI", "beqzcSb"
}
local map_pop76_r6 = {
maprs = maprs_popS, [0] = "jialcTI", "bnezcSb"
}
local function maprs_popST(rs, rt)
if rs >= rt then return 0 elseif rs == 0 then return 1 else return 2 end
end
local map_pop10_r6 = {
maprs = maprs_popST, [0] = "bovcSTB", "beqzalcTB", "beqcSTB"
}
local map_pop30_r6 = {
maprs = maprs_popST, [0] = "bnvcSTB", "bnezalcTB", "bnecSTB"
}
local map_pri_r6 = {
[0] = map_special_r6, map_regimm_r6, "jJ", "jalJ",
"beq|beqz|bST00B", "bne|bnezST0B", map_pop06_r6, map_pop07_r6,
map_pop10_r6, "addiu|liTS0I", "sltiTSI", "sltiuTSI",
"andiTSU", "ori|liTS0U", "xoriTSU", "aui|luiTS0U",
map_cop0, map_cop1_r6, false, false,
false, false, map_pop26_r6, map_pop27_r6,
map_pop30_r6, "daddiuTSI", false, false,
false, "dauiTSI", false, map_special3_r6,
"lbTSO", "lhTSO", false, "lwTSO",
"lbuTSO", "lhuTSO", false, false,
"sbTSO", "shTSO", false, "swTSO",
false, false, false, false,
false, "lwc1HSO", "bc#", false,
false, "ldc1HSO", map_pop66_r6, "ldTSO",
false, "swc1HSO", "balc#", map_pcrel_r6,
false, "sdc1HSO", map_pop76_r6, "sdTSO",
}
------------------------------------------------------------------------------
local map_gpr = {
[0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
"r24", "r25", "r26", "r27", "r28", "sp", "r30", "ra",
}
------------------------------------------------------------------------------
-- Output a nicely formatted line with an opcode and operands.
local function putop(ctx, text, operands)
local pos = ctx.pos
local extra = ""
if ctx.rel then
local sym = ctx.symtab[ctx.rel]
if sym then extra = "\t->"..sym end
end
if ctx.hexdump > 0 then
ctx.out(format("%08x %s %-7s %s%s\n",
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
else
ctx.out(format("%08x %-7s %s%s\n",
ctx.addr+pos, text, concat(operands, ", "), extra))
end
ctx.pos = pos + 4
end
-- Fallback for unknown opcodes.
local function unknown(ctx)
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
end
local function get_be(ctx)
local pos = ctx.pos
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
return bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
end
local function get_le(ctx)
local pos = ctx.pos
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
return bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
end
-- Disassemble a single instruction.
local function disass_ins(ctx)
local op = ctx:get()
local operands = {}
local last = nil
ctx.op = op
ctx.rel = nil
local opat = ctx.map_pri[rshift(op, 26)]
while type(opat) ~= "string" do
if not opat then return unknown(ctx) end
if opat.maprs then
opat = opat[opat.maprs(band(rshift(op,21),31), band(rshift(op,16),31))]
else
opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
end
end
local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)")
if altname then pat = pat2 end
for p in gmatch(pat, ".") do
local x = nil
if p == "S" then
x = map_gpr[band(rshift(op, 21), 31)]
elseif p == "T" then
x = map_gpr[band(rshift(op, 16), 31)]
elseif p == "D" then
x = map_gpr[band(rshift(op, 11), 31)]
elseif p == "F" then
x = "f"..band(rshift(op, 6), 31)
elseif p == "G" then
x = "f"..band(rshift(op, 11), 31)
elseif p == "H" then
x = "f"..band(rshift(op, 16), 31)
elseif p == "R" then
x = "f"..band(rshift(op, 21), 31)
elseif p == "A" then
x = band(rshift(op, 6), 31)
elseif p == "a" then
x = band(rshift(op, 6), 7)
elseif p == "E" then
x = band(rshift(op, 6), 31) + 32
elseif p == "M" then
x = band(rshift(op, 11), 31)
elseif p == "N" then
x = band(rshift(op, 16), 31)
elseif p == "C" then
x = band(rshift(op, 18), 7)
if x == 0 then x = nil end
elseif p == "K" then
x = band(rshift(op, 11), 31) + 1
elseif p == "P" then
x = band(rshift(op, 11), 31) + 33
elseif p == "L" then
x = band(rshift(op, 11), 31) - last + 1
elseif p == "Q" then
x = band(rshift(op, 11), 31) - last + 33
elseif p == "I" then
x = arshift(lshift(op, 16), 16)
elseif p == "2" then
x = arshift(lshift(op, 13), 11)
elseif p == "3" then
x = arshift(lshift(op, 14), 11)
elseif p == "U" then
x = band(op, 0xffff)
elseif p == "O" then
local disp = arshift(lshift(op, 16), 16)
operands[#operands] = format("%d(%s)", disp, last)
elseif p == "X" then
local index = map_gpr[band(rshift(op, 16), 31)]
operands[#operands] = format("%s(%s)", index, last)
elseif p == "B" then
x = ctx.addr + ctx.pos + arshift(lshift(op, 16), 14) + 4
ctx.rel = x
x = format("0x%08x", x)
elseif p == "b" then
x = ctx.addr + ctx.pos + arshift(lshift(op, 11), 9) + 4
ctx.rel = x
x = format("0x%08x", x)
elseif p == "#" then
x = ctx.addr + ctx.pos + arshift(lshift(op, 6), 4) + 4
ctx.rel = x
x = format("0x%08x", x)
elseif p == "J" then
local a = ctx.addr + ctx.pos
x = a - band(a, 0x0fffffff) + band(op, 0x03ffffff)*4
ctx.rel = x
x = format("0x%08x", x)
elseif p == "V" then
x = band(rshift(op, 8), 7)
if x == 0 then x = nil end
elseif p == "W" then
x = band(op, 7)
if x == 0 then x = nil end
elseif p == "Y" then
x = band(rshift(op, 6), 0x000fffff)
if x == 0 then x = nil end
elseif p == "Z" then
x = band(rshift(op, 6), 1023)
if x == 0 then x = nil end
elseif p == "0" then
if last == "r0" or last == 0 then
local n = #operands
operands[n] = nil
last = operands[n-1]
if altname then
local a1, a2 = match(altname, "([^|]*)|(.*)")
if a1 then name, altname = a1, a2
else name = altname end
end
end
elseif p == "1" then
if last == "ra" then
operands[#operands] = nil
end
else
assert(false)
end
if x then operands[#operands+1] = x; last = x end
end
return putop(ctx, name, operands)
end
------------------------------------------------------------------------------
-- Disassemble a block of code.
local function disass_block(ctx, ofs, len)
if not ofs then ofs = 0 end
local stop = len and ofs+len or #ctx.code
stop = stop - stop % 4
ctx.pos = ofs - ofs % 4
ctx.rel = nil
while ctx.pos < stop do disass_ins(ctx) end
end
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
local function create(code, addr, out)
local ctx = {}
ctx.code = code
ctx.addr = addr or 0
ctx.out = out or io.write
ctx.symtab = {}
ctx.disass = disass_block
ctx.hexdump = 8
ctx.get = get_be
ctx.map_pri = map_pri
return ctx
end
local function create_el(code, addr, out)
local ctx = create(code, addr, out)
ctx.get = get_le
return ctx
end
local function create_r6(code, addr, out)
local ctx = create(code, addr, out)
ctx.map_pri = map_pri_r6
return ctx
end
local function create_r6_el(code, addr, out)
local ctx = create(code, addr, out)
ctx.get = get_le
ctx.map_pri = map_pri_r6
return ctx
end
-- Simple API: disassemble code (a string) at address and output via out.
local function disass(code, addr, out)
create(code, addr, out):disass()
end
local function disass_el(code, addr, out)
create_el(code, addr, out):disass()
end
local function disass_r6(code, addr, out)
create_r6(code, addr, out):disass()
end
local function disass_r6_el(code, addr, out)
create_r6_el(code, addr, out):disass()
end
-- Return register name for RID.
local function regname(r)
if r < 32 then return map_gpr[r] end
return "f"..(r-32)
end
-- Public module functions.
return {
create = create,
create_el = create_el,
create_r6 = create_r6,
create_r6_el = create_r6_el,
disass = disass,
disass_el = disass_el,
disass_r6 = disass_r6,
disass_r6_el = disass_r6_el,
regname = regname
}

View file

@ -0,0 +1,17 @@
----------------------------------------------------------------------------
-- LuaJIT MIPS64 disassembler wrapper module.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This module just exports the big-endian functions from the
-- MIPS disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
return {
create = dis_mips.create,
disass = dis_mips.disass,
regname = dis_mips.regname
}

View file

@ -0,0 +1,17 @@
----------------------------------------------------------------------------
-- LuaJIT MIPS64EL disassembler wrapper module.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This module just exports the little-endian functions from the
-- MIPS disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
return {
create = dis_mips.create_el,
disass = dis_mips.disass_el,
regname = dis_mips.regname
}

View file

@ -0,0 +1,17 @@
----------------------------------------------------------------------------
-- LuaJIT MIPS64R6 disassembler wrapper module.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This module just exports the r6 big-endian functions from the
-- MIPS disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
return {
create = dis_mips.create_r6,
disass = dis_mips.disass_r6,
regname = dis_mips.regname
}

View file

@ -0,0 +1,17 @@
----------------------------------------------------------------------------
-- LuaJIT MIPS64R6EL disassembler wrapper module.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This module just exports the r6 little-endian functions from the
-- MIPS disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
return {
create = dis_mips.create_r6_el,
disass = dis_mips.disass_r6_el,
regname = dis_mips.regname
}

View file

@ -0,0 +1,17 @@
----------------------------------------------------------------------------
-- LuaJIT MIPSEL disassembler wrapper module.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This module just exports the little-endian functions from the
-- MIPS disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
return {
create = dis_mips.create_el,
disass = dis_mips.disass_el,
regname = dis_mips.regname
}

View file

@ -0,0 +1,591 @@
----------------------------------------------------------------------------
-- LuaJIT PPC disassembler module.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT/X license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This is a helper module used by the LuaJIT machine code dumper module.
--
-- It disassembles all common, non-privileged 32/64 bit PowerPC instructions
-- plus the e500 SPE instructions and some Cell/Xenon extensions.
--
-- NYI: VMX, VMX128
------------------------------------------------------------------------------
local type = type
local byte, format = string.byte, string.format
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
local concat = table.concat
local bit = require("bit")
local band, bor, tohex = bit.band, bit.bor, bit.tohex
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
------------------------------------------------------------------------------
-- Primary and extended opcode maps
------------------------------------------------------------------------------
local map_crops = {
shift = 1, mask = 1023,
[0] = "mcrfXX",
[33] = "crnor|crnotCCC=", [129] = "crandcCCC",
[193] = "crxor|crclrCCC%", [225] = "crnandCCC",
[257] = "crandCCC", [289] = "creqv|crsetCCC%",
[417] = "crorcCCC", [449] = "cror|crmoveCCC=",
[16] = "b_lrKB", [528] = "b_ctrKB",
[150] = "isync",
}
local map_rlwinm = setmetatable({
shift = 0, mask = -1,
},
{ __index = function(t, x)
local rot = band(rshift(x, 11), 31)
local mb = band(rshift(x, 6), 31)
local me = band(rshift(x, 1), 31)
if mb == 0 and me == 31-rot then
return "slwiRR~A."
elseif me == 31 and mb == 32-rot then
return "srwiRR~-A."
else
return "rlwinmRR~AAA."
end
end
})
local map_rld = {
shift = 2, mask = 7,
[0] = "rldiclRR~HM.", "rldicrRR~HM.", "rldicRR~HM.", "rldimiRR~HM.",
{
shift = 1, mask = 1,
[0] = "rldclRR~RM.", "rldcrRR~RM.",
},
}
local map_ext = setmetatable({
shift = 1, mask = 1023,
[0] = "cmp_YLRR", [32] = "cmpl_YLRR",
[4] = "twARR", [68] = "tdARR",
[8] = "subfcRRR.", [40] = "subfRRR.",
[104] = "negRR.", [136] = "subfeRRR.",
[200] = "subfzeRR.", [232] = "subfmeRR.",
[520] = "subfcoRRR.", [552] = "subfoRRR.",
[616] = "negoRR.", [648] = "subfeoRRR.",
[712] = "subfzeoRR.", [744] = "subfmeoRR.",
[9] = "mulhduRRR.", [73] = "mulhdRRR.", [233] = "mulldRRR.",
[457] = "divduRRR.", [489] = "divdRRR.",
[745] = "mulldoRRR.",
[969] = "divduoRRR.", [1001] = "divdoRRR.",
[10] = "addcRRR.", [138] = "addeRRR.",
[202] = "addzeRR.", [234] = "addmeRR.", [266] = "addRRR.",
[522] = "addcoRRR.", [650] = "addeoRRR.",
[714] = "addzeoRR.", [746] = "addmeoRR.", [778] = "addoRRR.",
[11] = "mulhwuRRR.", [75] = "mulhwRRR.", [235] = "mullwRRR.",
[459] = "divwuRRR.", [491] = "divwRRR.",
[747] = "mullwoRRR.",
[971] = "divwouRRR.", [1003] = "divwoRRR.",
[15] = "iselltRRR", [47] = "iselgtRRR", [79] = "iseleqRRR",
[144] = { shift = 20, mask = 1, [0] = "mtcrfRZ~", "mtocrfRZ~", },
[19] = { shift = 20, mask = 1, [0] = "mfcrR", "mfocrfRZ", },
[371] = { shift = 11, mask = 1023, [392] = "mftbR", [424] = "mftbuR", },
[339] = {
shift = 11, mask = 1023,
[32] = "mferR", [256] = "mflrR", [288] = "mfctrR", [16] = "mfspefscrR",
},
[467] = {
shift = 11, mask = 1023,
[32] = "mtxerR", [256] = "mtlrR", [288] = "mtctrR", [16] = "mtspefscrR",
},
[20] = "lwarxRR0R", [84] = "ldarxRR0R",
[21] = "ldxRR0R", [53] = "lduxRRR",
[149] = "stdxRR0R", [181] = "stduxRRR",
[341] = "lwaxRR0R", [373] = "lwauxRRR",
[23] = "lwzxRR0R", [55] = "lwzuxRRR",
[87] = "lbzxRR0R", [119] = "lbzuxRRR",
[151] = "stwxRR0R", [183] = "stwuxRRR",
[215] = "stbxRR0R", [247] = "stbuxRRR",
[279] = "lhzxRR0R", [311] = "lhzuxRRR",
[343] = "lhaxRR0R", [375] = "lhauxRRR",
[407] = "sthxRR0R", [439] = "sthuxRRR",
[54] = "dcbst-R0R", [86] = "dcbf-R0R",
[150] = "stwcxRR0R.", [214] = "stdcxRR0R.",
[246] = "dcbtst-R0R", [278] = "dcbt-R0R",
[310] = "eciwxRR0R", [438] = "ecowxRR0R",
[470] = "dcbi-RR",
[598] = {
shift = 21, mask = 3,
[0] = "sync", "lwsync", "ptesync",
},
[758] = "dcba-RR",
[854] = "eieio", [982] = "icbi-R0R", [1014] = "dcbz-R0R",
[26] = "cntlzwRR~", [58] = "cntlzdRR~",
[122] = "popcntbRR~",
[154] = "prtywRR~", [186] = "prtydRR~",
[28] = "andRR~R.", [60] = "andcRR~R.", [124] = "nor|notRR~R=.",
[284] = "eqvRR~R.", [316] = "xorRR~R.",
[412] = "orcRR~R.", [444] = "or|mrRR~R=.", [476] = "nandRR~R.",
[508] = "cmpbRR~R",
[512] = "mcrxrX",
[532] = "ldbrxRR0R", [660] = "stdbrxRR0R",
[533] = "lswxRR0R", [597] = "lswiRR0A",
[661] = "stswxRR0R", [725] = "stswiRR0A",
[534] = "lwbrxRR0R", [662] = "stwbrxRR0R",
[790] = "lhbrxRR0R", [918] = "sthbrxRR0R",
[535] = "lfsxFR0R", [567] = "lfsuxFRR",
[599] = "lfdxFR0R", [631] = "lfduxFRR",
[663] = "stfsxFR0R", [695] = "stfsuxFRR",
[727] = "stfdxFR0R", [759] = "stfduxFR0R",
[855] = "lfiwaxFR0R",
[983] = "stfiwxFR0R",
[24] = "slwRR~R.",
[27] = "sldRR~R.", [536] = "srwRR~R.",
[792] = "srawRR~R.", [824] = "srawiRR~A.",
[794] = "sradRR~R.", [826] = "sradiRR~H.", [827] = "sradiRR~H.",
[922] = "extshRR~.", [954] = "extsbRR~.", [986] = "extswRR~.",
[539] = "srdRR~R.",
},
{ __index = function(t, x)
if band(x, 31) == 15 then return "iselRRRC" end
end
})
local map_ld = {
shift = 0, mask = 3,
[0] = "ldRRE", "lduRRE", "lwaRRE",
}
local map_std = {
shift = 0, mask = 3,
[0] = "stdRRE", "stduRRE",
}
local map_fps = {
shift = 5, mask = 1,
{
shift = 1, mask = 15,
[0] = false, false, "fdivsFFF.", false,
"fsubsFFF.", "faddsFFF.", "fsqrtsF-F.", false,
"fresF-F.", "fmulsFF-F.", "frsqrtesF-F.", false,
"fmsubsFFFF~.", "fmaddsFFFF~.", "fnmsubsFFFF~.", "fnmaddsFFFF~.",
}
}
local map_fpd = {
shift = 5, mask = 1,
[0] = {
shift = 1, mask = 1023,
[0] = "fcmpuXFF", [32] = "fcmpoXFF", [64] = "mcrfsXX",
[38] = "mtfsb1A.", [70] = "mtfsb0A.", [134] = "mtfsfiA>>-A>",
[8] = "fcpsgnFFF.", [40] = "fnegF-F.", [72] = "fmrF-F.",
[136] = "fnabsF-F.", [264] = "fabsF-F.",
[12] = "frspF-F.",
[14] = "fctiwF-F.", [15] = "fctiwzF-F.",
[583] = "mffsF.", [711] = "mtfsfZF.",
[392] = "frinF-F.", [424] = "frizF-F.",
[456] = "fripF-F.", [488] = "frimF-F.",
[814] = "fctidF-F.", [815] = "fctidzF-F.", [846] = "fcfidF-F.",
},
{
shift = 1, mask = 15,
[0] = false, false, "fdivFFF.", false,
"fsubFFF.", "faddFFF.", "fsqrtF-F.", "fselFFFF~.",
"freF-F.", "fmulFF-F.", "frsqrteF-F.", false,
"fmsubFFFF~.", "fmaddFFFF~.", "fnmsubFFFF~.", "fnmaddFFFF~.",
}
}
local map_spe = {
shift = 0, mask = 2047,
[512] = "evaddwRRR", [514] = "evaddiwRAR~",
[516] = "evsubwRRR~", [518] = "evsubiwRAR~",
[520] = "evabsRR", [521] = "evnegRR",
[522] = "evextsbRR", [523] = "evextshRR", [524] = "evrndwRR",
[525] = "evcntlzwRR", [526] = "evcntlswRR",
[527] = "brincRRR",
[529] = "evandRRR", [530] = "evandcRRR", [534] = "evxorRRR",
[535] = "evor|evmrRRR=", [536] = "evnor|evnotRRR=",
[537] = "eveqvRRR", [539] = "evorcRRR", [542] = "evnandRRR",
[544] = "evsrwuRRR", [545] = "evsrwsRRR",
[546] = "evsrwiuRRA", [547] = "evsrwisRRA",
[548] = "evslwRRR", [550] = "evslwiRRA",
[552] = "evrlwRRR", [553] = "evsplatiRS",
[554] = "evrlwiRRA", [555] = "evsplatfiRS",
[556] = "evmergehiRRR", [557] = "evmergeloRRR",
[558] = "evmergehiloRRR", [559] = "evmergelohiRRR",
[560] = "evcmpgtuYRR", [561] = "evcmpgtsYRR",
[562] = "evcmpltuYRR", [563] = "evcmpltsYRR",
[564] = "evcmpeqYRR",
[632] = "evselRRR", [633] = "evselRRRW",
[634] = "evselRRRW", [635] = "evselRRRW",
[636] = "evselRRRW", [637] = "evselRRRW",
[638] = "evselRRRW", [639] = "evselRRRW",
[640] = "evfsaddRRR", [641] = "evfssubRRR",
[644] = "evfsabsRR", [645] = "evfsnabsRR", [646] = "evfsnegRR",
[648] = "evfsmulRRR", [649] = "evfsdivRRR",
[652] = "evfscmpgtYRR", [653] = "evfscmpltYRR", [654] = "evfscmpeqYRR",
[656] = "evfscfuiR-R", [657] = "evfscfsiR-R",
[658] = "evfscfufR-R", [659] = "evfscfsfR-R",
[660] = "evfsctuiR-R", [661] = "evfsctsiR-R",
[662] = "evfsctufR-R", [663] = "evfsctsfR-R",
[664] = "evfsctuizR-R", [666] = "evfsctsizR-R",
[668] = "evfststgtYRR", [669] = "evfststltYRR", [670] = "evfststeqYRR",
[704] = "efsaddRRR", [705] = "efssubRRR",
[708] = "efsabsRR", [709] = "efsnabsRR", [710] = "efsnegRR",
[712] = "efsmulRRR", [713] = "efsdivRRR",
[716] = "efscmpgtYRR", [717] = "efscmpltYRR", [718] = "efscmpeqYRR",
[719] = "efscfdR-R",
[720] = "efscfuiR-R", [721] = "efscfsiR-R",
[722] = "efscfufR-R", [723] = "efscfsfR-R",
[724] = "efsctuiR-R", [725] = "efsctsiR-R",
[726] = "efsctufR-R", [727] = "efsctsfR-R",
[728] = "efsctuizR-R", [730] = "efsctsizR-R",
[732] = "efststgtYRR", [733] = "efststltYRR", [734] = "efststeqYRR",
[736] = "efdaddRRR", [737] = "efdsubRRR",
[738] = "efdcfuidR-R", [739] = "efdcfsidR-R",
[740] = "efdabsRR", [741] = "efdnabsRR", [742] = "efdnegRR",
[744] = "efdmulRRR", [745] = "efddivRRR",
[746] = "efdctuidzR-R", [747] = "efdctsidzR-R",
[748] = "efdcmpgtYRR", [749] = "efdcmpltYRR", [750] = "efdcmpeqYRR",
[751] = "efdcfsR-R",
[752] = "efdcfuiR-R", [753] = "efdcfsiR-R",
[754] = "efdcfufR-R", [755] = "efdcfsfR-R",
[756] = "efdctuiR-R", [757] = "efdctsiR-R",
[758] = "efdctufR-R", [759] = "efdctsfR-R",
[760] = "efdctuizR-R", [762] = "efdctsizR-R",
[764] = "efdtstgtYRR", [765] = "efdtstltYRR", [766] = "efdtsteqYRR",
[768] = "evlddxRR0R", [769] = "evlddRR8",
[770] = "evldwxRR0R", [771] = "evldwRR8",
[772] = "evldhxRR0R", [773] = "evldhRR8",
[776] = "evlhhesplatxRR0R", [777] = "evlhhesplatRR2",
[780] = "evlhhousplatxRR0R", [781] = "evlhhousplatRR2",
[782] = "evlhhossplatxRR0R", [783] = "evlhhossplatRR2",
[784] = "evlwhexRR0R", [785] = "evlwheRR4",
[788] = "evlwhouxRR0R", [789] = "evlwhouRR4",
[790] = "evlwhosxRR0R", [791] = "evlwhosRR4",
[792] = "evlwwsplatxRR0R", [793] = "evlwwsplatRR4",
[796] = "evlwhsplatxRR0R", [797] = "evlwhsplatRR4",
[800] = "evstddxRR0R", [801] = "evstddRR8",
[802] = "evstdwxRR0R", [803] = "evstdwRR8",
[804] = "evstdhxRR0R", [805] = "evstdhRR8",
[816] = "evstwhexRR0R", [817] = "evstwheRR4",
[820] = "evstwhoxRR0R", [821] = "evstwhoRR4",
[824] = "evstwwexRR0R", [825] = "evstwweRR4",
[828] = "evstwwoxRR0R", [829] = "evstwwoRR4",
[1027] = "evmhessfRRR", [1031] = "evmhossfRRR", [1032] = "evmheumiRRR",
[1033] = "evmhesmiRRR", [1035] = "evmhesmfRRR", [1036] = "evmhoumiRRR",
[1037] = "evmhosmiRRR", [1039] = "evmhosmfRRR", [1059] = "evmhessfaRRR",
[1063] = "evmhossfaRRR", [1064] = "evmheumiaRRR", [1065] = "evmhesmiaRRR",
[1067] = "evmhesmfaRRR", [1068] = "evmhoumiaRRR", [1069] = "evmhosmiaRRR",
[1071] = "evmhosmfaRRR", [1095] = "evmwhssfRRR", [1096] = "evmwlumiRRR",
[1100] = "evmwhumiRRR", [1101] = "evmwhsmiRRR", [1103] = "evmwhsmfRRR",
[1107] = "evmwssfRRR", [1112] = "evmwumiRRR", [1113] = "evmwsmiRRR",
[1115] = "evmwsmfRRR", [1127] = "evmwhssfaRRR", [1128] = "evmwlumiaRRR",
[1132] = "evmwhumiaRRR", [1133] = "evmwhsmiaRRR", [1135] = "evmwhsmfaRRR",
[1139] = "evmwssfaRRR", [1144] = "evmwumiaRRR", [1145] = "evmwsmiaRRR",
[1147] = "evmwsmfaRRR",
[1216] = "evaddusiaawRR", [1217] = "evaddssiaawRR",
[1218] = "evsubfusiaawRR", [1219] = "evsubfssiaawRR",
[1220] = "evmraRR",
[1222] = "evdivwsRRR", [1223] = "evdivwuRRR",
[1224] = "evaddumiaawRR", [1225] = "evaddsmiaawRR",
[1226] = "evsubfumiaawRR", [1227] = "evsubfsmiaawRR",
[1280] = "evmheusiaawRRR", [1281] = "evmhessiaawRRR",
[1283] = "evmhessfaawRRR", [1284] = "evmhousiaawRRR",
[1285] = "evmhossiaawRRR", [1287] = "evmhossfaawRRR",
[1288] = "evmheumiaawRRR", [1289] = "evmhesmiaawRRR",
[1291] = "evmhesmfaawRRR", [1292] = "evmhoumiaawRRR",
[1293] = "evmhosmiaawRRR", [1295] = "evmhosmfaawRRR",
[1320] = "evmhegumiaaRRR", [1321] = "evmhegsmiaaRRR",
[1323] = "evmhegsmfaaRRR", [1324] = "evmhogumiaaRRR",
[1325] = "evmhogsmiaaRRR", [1327] = "evmhogsmfaaRRR",
[1344] = "evmwlusiaawRRR", [1345] = "evmwlssiaawRRR",
[1352] = "evmwlumiaawRRR", [1353] = "evmwlsmiaawRRR",
[1363] = "evmwssfaaRRR", [1368] = "evmwumiaaRRR",
[1369] = "evmwsmiaaRRR", [1371] = "evmwsmfaaRRR",
[1408] = "evmheusianwRRR", [1409] = "evmhessianwRRR",
[1411] = "evmhessfanwRRR", [1412] = "evmhousianwRRR",
[1413] = "evmhossianwRRR", [1415] = "evmhossfanwRRR",
[1416] = "evmheumianwRRR", [1417] = "evmhesmianwRRR",
[1419] = "evmhesmfanwRRR", [1420] = "evmhoumianwRRR",
[1421] = "evmhosmianwRRR", [1423] = "evmhosmfanwRRR",
[1448] = "evmhegumianRRR", [1449] = "evmhegsmianRRR",
[1451] = "evmhegsmfanRRR", [1452] = "evmhogumianRRR",
[1453] = "evmhogsmianRRR", [1455] = "evmhogsmfanRRR",
[1472] = "evmwlusianwRRR", [1473] = "evmwlssianwRRR",
[1480] = "evmwlumianwRRR", [1481] = "evmwlsmianwRRR",
[1491] = "evmwssfanRRR", [1496] = "evmwumianRRR",
[1497] = "evmwsmianRRR", [1499] = "evmwsmfanRRR",
}
local map_pri = {
[0] = false, false, "tdiARI", "twiARI",
map_spe, false, false, "mulliRRI",
"subficRRI", false, "cmpl_iYLRU", "cmp_iYLRI",
"addicRRI", "addic.RRI", "addi|liRR0I", "addis|lisRR0I",
"b_KBJ", "sc", "bKJ", map_crops,
"rlwimiRR~AAA.", map_rlwinm, false, "rlwnmRR~RAA.",
"oriNRR~U", "orisRR~U", "xoriRR~U", "xorisRR~U",
"andi.RR~U", "andis.RR~U", map_rld, map_ext,
"lwzRRD", "lwzuRRD", "lbzRRD", "lbzuRRD",
"stwRRD", "stwuRRD", "stbRRD", "stbuRRD",
"lhzRRD", "lhzuRRD", "lhaRRD", "lhauRRD",
"sthRRD", "sthuRRD", "lmwRRD", "stmwRRD",
"lfsFRD", "lfsuFRD", "lfdFRD", "lfduFRD",
"stfsFRD", "stfsuFRD", "stfdFRD", "stfduFRD",
false, false, map_ld, map_fps,
false, false, map_std, map_fpd,
}
------------------------------------------------------------------------------
local map_gpr = {
[0] = "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
}
local map_cond = { [0] = "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", }
-- Format a condition bit.
local function condfmt(cond)
if cond <= 3 then
return map_cond[band(cond, 3)]
else
return format("4*cr%d+%s", rshift(cond, 2), map_cond[band(cond, 3)])
end
end
------------------------------------------------------------------------------
-- Output a nicely formatted line with an opcode and operands.
local function putop(ctx, text, operands)
local pos = ctx.pos
local extra = ""
if ctx.rel then
local sym = ctx.symtab[ctx.rel]
if sym then extra = "\t->"..sym end
end
if ctx.hexdump > 0 then
ctx.out(format("%08x %s %-7s %s%s\n",
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
else
ctx.out(format("%08x %-7s %s%s\n",
ctx.addr+pos, text, concat(operands, ", "), extra))
end
ctx.pos = pos + 4
end
-- Fallback for unknown opcodes.
local function unknown(ctx)
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
end
-- Disassemble a single instruction.
local function disass_ins(ctx)
local pos = ctx.pos
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
local op = bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
local operands = {}
local last = nil
local rs = 21
ctx.op = op
ctx.rel = nil
local opat = map_pri[rshift(b0, 2)]
while type(opat) ~= "string" do
if not opat then return unknown(ctx) end
opat = opat[band(rshift(op, opat.shift), opat.mask)]
end
local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
local altname, pat2 = match(pat, "|([a-z0-9_.]*)(.*)")
if altname then pat = pat2 end
for p in gmatch(pat, ".") do
local x = nil
if p == "R" then
x = map_gpr[band(rshift(op, rs), 31)]
rs = rs - 5
elseif p == "F" then
x = "f"..band(rshift(op, rs), 31)
rs = rs - 5
elseif p == "A" then
x = band(rshift(op, rs), 31)
rs = rs - 5
elseif p == "S" then
x = arshift(lshift(op, 27-rs), 27)
rs = rs - 5
elseif p == "I" then
x = arshift(lshift(op, 16), 16)
elseif p == "U" then
x = band(op, 0xffff)
elseif p == "D" or p == "E" then
local disp = arshift(lshift(op, 16), 16)
if p == "E" then disp = band(disp, -4) end
if last == "r0" then last = "0" end
operands[#operands] = format("%d(%s)", disp, last)
elseif p >= "2" and p <= "8" then
local disp = band(rshift(op, rs), 31) * p
if last == "r0" then last = "0" end
operands[#operands] = format("%d(%s)", disp, last)
elseif p == "H" then
x = band(rshift(op, rs), 31) + lshift(band(op, 2), 4)
rs = rs - 5
elseif p == "M" then
x = band(rshift(op, rs), 31) + band(op, 0x20)
elseif p == "C" then
x = condfmt(band(rshift(op, rs), 31))
rs = rs - 5
elseif p == "B" then
local bo = rshift(op, 21)
local cond = band(rshift(op, 16), 31)
local cn = ""
rs = rs - 10
if band(bo, 4) == 0 then
cn = band(bo, 2) == 0 and "dnz" or "dz"
if band(bo, 0x10) == 0 then
cn = cn..(band(bo, 8) == 0 and "f" or "t")
end
if band(bo, 0x10) == 0 then x = condfmt(cond) end
name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
elseif band(bo, 0x10) == 0 then
cn = map_cond[band(cond, 3) + (band(bo, 8) == 0 and 4 or 0)]
if cond > 3 then x = "cr"..rshift(cond, 2) end
name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
end
name = gsub(name, "_", cn)
elseif p == "J" then
x = arshift(lshift(op, 27-rs), 29-rs)*4
if band(op, 2) == 0 then x = ctx.addr + pos + x end
ctx.rel = x
x = "0x"..tohex(x)
elseif p == "K" then
if band(op, 1) ~= 0 then name = name.."l" end
if band(op, 2) ~= 0 then name = name.."a" end
elseif p == "X" or p == "Y" then
x = band(rshift(op, rs+2), 7)
if x == 0 and p == "Y" then x = nil else x = "cr"..x end
rs = rs - 5
elseif p == "W" then
x = "cr"..band(op, 7)
elseif p == "Z" then
x = band(rshift(op, rs-4), 255)
rs = rs - 10
elseif p == ">" then
operands[#operands] = rshift(operands[#operands], 1)
elseif p == "0" then
if last == "r0" then
operands[#operands] = nil
if altname then name = altname end
end
elseif p == "L" then
name = gsub(name, "_", band(op, 0x00200000) ~= 0 and "d" or "w")
elseif p == "." then
if band(op, 1) == 1 then name = name.."." end
elseif p == "N" then
if op == 0x60000000 then name = "nop"; break end
elseif p == "~" then
local n = #operands
operands[n-1], operands[n] = operands[n], operands[n-1]
elseif p == "=" then
local n = #operands
if last == operands[n-1] then
operands[n] = nil
name = altname
end
elseif p == "%" then
local n = #operands
if last == operands[n-1] and last == operands[n-2] then
operands[n] = nil
operands[n-1] = nil
name = altname
end
elseif p == "-" then
rs = rs - 5
else
assert(false)
end
if x then operands[#operands+1] = x; last = x end
end
return putop(ctx, name, operands)
end
------------------------------------------------------------------------------
-- Disassemble a block of code.
local function disass_block(ctx, ofs, len)
if not ofs then ofs = 0 end
local stop = len and ofs+len or #ctx.code
stop = stop - stop % 4
ctx.pos = ofs - ofs % 4
ctx.rel = nil
while ctx.pos < stop do disass_ins(ctx) end
end
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
local function create(code, addr, out)
local ctx = {}
ctx.code = code
ctx.addr = addr or 0
ctx.out = out or io.write
ctx.symtab = {}
ctx.disass = disass_block
ctx.hexdump = 8
return ctx
end
-- Simple API: disassemble code (a string) at address and output via out.
local function disass(code, addr, out)
create(code, addr, out):disass()
end
-- Return register name for RID.
local function regname(r)
if r < 32 then return map_gpr[r] end
return "f"..(r-32)
end
-- Public module functions.
return {
create = create,
disass = disass,
regname = regname
}

View file

@ -0,0 +1,17 @@
----------------------------------------------------------------------------
-- LuaJIT x64 disassembler wrapper module.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This module just exports the 64 bit functions from the combined
-- x86/x64 disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
local dis_x86 = require((string.match(..., ".*%.") or "").."dis_x86")
return {
create = dis_x86.create64,
disass = dis_x86.disass64,
regname = dis_x86.regname64
}

View file

@ -0,0 +1,953 @@
----------------------------------------------------------------------------
-- LuaJIT x86/x64 disassembler module.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This is a helper module used by the LuaJIT machine code dumper module.
--
-- Sending small code snippets to an external disassembler and mixing the
-- output with our own stuff was too fragile. So I had to bite the bullet
-- and write yet another x86 disassembler. Oh well ...
--
-- The output format is very similar to what ndisasm generates. But it has
-- been developed independently by looking at the opcode tables from the
-- Intel and AMD manuals. The supported instruction set is quite extensive
-- and reflects what a current generation Intel or AMD CPU implements in
-- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3,
-- SSE4.1, SSE4.2, SSE4a, AVX, AVX2 and even privileged and hypervisor
-- (VMX/SVM) instructions.
--
-- Notes:
-- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported.
-- * No attempt at optimization has been made -- it's fast enough for my needs.
------------------------------------------------------------------------------
local type = type
local sub, byte, format = string.sub, string.byte, string.format
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
local lower, rep = string.lower, string.rep
local bit = require("bit")
local tohex = bit.tohex
-- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on.
local map_opc1_32 = {
--0x
[0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es",
"orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*",
--1x
"adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss",
"sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds",
--2x
"andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa",
"subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das",
--3x
"xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa",
"cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas",
--4x
"incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR",
"decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR",
--5x
"pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR",
"popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR",
--6x
"sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr",
"fs:seg","gs:seg","o16:","a16",
"pushUi","imulVrmi","pushBs","imulVrms",
"insb","insVS","outsb","outsVS",
--7x
"joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj",
"jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj",
--8x
"arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms",
"testBmr","testVmr","xchgBrm","xchgVrm",
"movBmr","movVmr","movBrm","movVrm",
"movVmg","leaVrm","movWgm","popUm",
--9x
"nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR",
"xchgVaR","xchgVaR","xchgVaR","xchgVaR",
"sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait",
"sz*pushfw,pushf","sz*popfw,popf","sahf","lahf",
--Ax
"movBao","movVao","movBoa","movVoa",
"movsb","movsVS","cmpsb","cmpsVS",
"testBai","testVai","stosb","stosVS",
"lodsb","lodsVS","scasb","scasVS",
--Bx
"movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi",
"movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI",
--Cx
"shift!Bmu","shift!Vmu","retBw","ret","vex*3$lesVrm","vex*2$ldsVrm","movBmi","movVmi",
"enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS",
--Dx
"shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb",
"fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7",
--Ex
"loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj",
"inBau","inVau","outBua","outVua",
"callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda",
--Fx
"lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm",
"clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm",
}
assert(#map_opc1_32 == 255)
-- Map for 1st opcode byte in 64 bit mode (overrides only).
local map_opc1_64 = setmetatable({
[0x06]=false, [0x07]=false, [0x0e]=false,
[0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false,
[0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false,
[0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:",
[0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb",
[0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb",
[0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb",
[0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb",
[0x82]=false, [0x9a]=false, [0xc4]="vex*3", [0xc5]="vex*2", [0xce]=false,
[0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false,
}, { __index = map_opc1_32 })
-- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you.
-- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2
local map_opc2 = {
--0x
[0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret",
"invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu",
--1x
"movupsXrm|movssXrvm|movupdXrm|movsdXrvm",
"movupsXmr|movssXmvr|movupdXmr|movsdXmvr",
"movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm",
"movlpsXmr||movlpdXmr",
"unpcklpsXrvm||unpcklpdXrvm",
"unpckhpsXrvm||unpckhpdXrvm",
"movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm",
"movhpsXmr||movhpdXmr",
"$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm",
"hintnopVm","hintnopVm","hintnopVm","hintnopVm",
--2x
"movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil,
"movapsXrm||movapdXrm",
"movapsXmr||movapdXmr",
"cvtpi2psXrMm|cvtsi2ssXrvVmt|cvtpi2pdXrMm|cvtsi2sdXrvVmt",
"movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr",
"cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm",
"cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm",
"ucomissXrm||ucomisdXrm",
"comissXrm||comisdXrm",
--3x
"wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec",
"opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil,
--4x
"cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm",
"cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm",
"cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm",
"cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm",
--5x
"movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm",
"rsqrtpsXrm|rsqrtssXrvm","rcppsXrm|rcpssXrvm",
"andpsXrvm||andpdXrvm","andnpsXrvm||andnpdXrvm",
"orpsXrvm||orpdXrvm","xorpsXrvm||xorpdXrvm",
"addpsXrvm|addssXrvm|addpdXrvm|addsdXrvm","mulpsXrvm|mulssXrvm|mulpdXrvm|mulsdXrvm",
"cvtps2pdXrm|cvtss2sdXrvm|cvtpd2psXrm|cvtsd2ssXrvm",
"cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm",
"subpsXrvm|subssXrvm|subpdXrvm|subsdXrvm","minpsXrvm|minssXrvm|minpdXrvm|minsdXrvm",
"divpsXrvm|divssXrvm|divpdXrvm|divsdXrvm","maxpsXrvm|maxssXrvm|maxpdXrvm|maxsdXrvm",
--6x
"punpcklbwPrvm","punpcklwdPrvm","punpckldqPrvm","packsswbPrvm",
"pcmpgtbPrvm","pcmpgtwPrvm","pcmpgtdPrvm","packuswbPrvm",
"punpckhbwPrvm","punpckhwdPrvm","punpckhdqPrvm","packssdwPrvm",
"||punpcklqdqXrvm","||punpckhqdqXrvm",
"movPrVSm","movqMrm|movdquXrm|movdqaXrm",
--7x
"pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pvmu",
"pshiftd!Pvmu","pshiftq!Mvmu||pshiftdq!Xvmu",
"pcmpeqbPrvm","pcmpeqwPrvm","pcmpeqdPrvm","emms*|",
"vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$",
nil,nil,
"||haddpdXrvm|haddpsXrvm","||hsubpdXrvm|hsubpsXrvm",
"movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr",
--8x
"joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj",
"jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj",
--9x
"setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm",
"setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm",
--Ax
"push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil,
"push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm",
--Bx
"cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr",
"$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt",
"|popcntVrm","ud2Dp","bt!Vmu","btcVmr",
"bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt",
--Cx
"xaddBmr","xaddVmr",
"cmppsXrvmu|cmpssXrvmu|cmppdXrvmu|cmpsdXrvmu","$movntiVmr|",
"pinsrwPrvWmu","pextrwDrPmu",
"shufpsXrvmu||shufpdXrvmu","$cmpxchg!Qmp",
"bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR",
--Dx
"||addsubpdXrvm|addsubpsXrvm","psrlwPrvm","psrldPrvm","psrlqPrvm",
"paddqPrvm","pmullwPrvm",
"|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm",
"psubusbPrvm","psubuswPrvm","pminubPrvm","pandPrvm",
"paddusbPrvm","padduswPrvm","pmaxubPrvm","pandnPrvm",
--Ex
"pavgbPrvm","psrawPrvm","psradPrvm","pavgwPrvm",
"pmulhuwPrvm","pmulhwPrvm",
"|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr",
"psubsbPrvm","psubswPrvm","pminswPrvm","porPrvm",
"paddsbPrvm","paddswPrvm","pmaxswPrvm","pxorPrvm",
--Fx
"|||lddquXrm","psllwPrvm","pslldPrvm","psllqPrvm",
"pmuludqPrvm","pmaddwdPrvm","psadbwPrvm","maskmovqMrm||maskmovdquXrm$",
"psubbPrvm","psubwPrvm","psubdPrvm","psubqPrvm",
"paddbPrvm","paddwPrvm","padddPrvm","ud",
}
assert(map_opc2[255] == "ud")
-- Map for three-byte opcodes. Can't wait for their next invention.
local map_opc3 = {
["38"] = { -- [66] 0f 38 xx
--0x
[0]="pshufbPrvm","phaddwPrvm","phadddPrvm","phaddswPrvm",
"pmaddubswPrvm","phsubwPrvm","phsubdPrvm","phsubswPrvm",
"psignbPrvm","psignwPrvm","psigndPrvm","pmulhrswPrvm",
"||permilpsXrvm","||permilpdXrvm",nil,nil,
--1x
"||pblendvbXrma",nil,nil,nil,
"||blendvpsXrma","||blendvpdXrma","||permpsXrvm","||ptestXrm",
"||broadcastssXrm","||broadcastsdXrm","||broadcastf128XrlXm",nil,
"pabsbPrm","pabswPrm","pabsdPrm",nil,
--2x
"||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm",
"||pmovsxwqXrm","||pmovsxdqXrm",nil,nil,
"||pmuldqXrvm","||pcmpeqqXrvm","||$movntdqaXrm","||packusdwXrvm",
"||maskmovpsXrvm","||maskmovpdXrvm","||maskmovpsXmvr","||maskmovpdXmvr",
--3x
"||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm",
"||pmovzxwqXrm","||pmovzxdqXrm","||permdXrvm","||pcmpgtqXrvm",
"||pminsbXrvm","||pminsdXrvm","||pminuwXrvm","||pminudXrvm",
"||pmaxsbXrvm","||pmaxsdXrvm","||pmaxuwXrvm","||pmaxudXrvm",
--4x
"||pmulddXrvm","||phminposuwXrm",nil,nil,
nil,"||psrlvVSXrvm","||psravdXrvm","||psllvVSXrvm",
--5x
[0x58] = "||pbroadcastdXrlXm",[0x59] = "||pbroadcastqXrlXm",
[0x5a] = "||broadcasti128XrlXm",
--7x
[0x78] = "||pbroadcastbXrlXm",[0x79] = "||pbroadcastwXrlXm",
--8x
[0x8c] = "||pmaskmovXrvVSm",
[0x8e] = "||pmaskmovVSmXvr",
--9x
[0x96] = "||fmaddsub132pHXrvm",[0x97] = "||fmsubadd132pHXrvm",
[0x98] = "||fmadd132pHXrvm",[0x99] = "||fmadd132sHXrvm",
[0x9a] = "||fmsub132pHXrvm",[0x9b] = "||fmsub132sHXrvm",
[0x9c] = "||fnmadd132pHXrvm",[0x9d] = "||fnmadd132sHXrvm",
[0x9e] = "||fnmsub132pHXrvm",[0x9f] = "||fnmsub132sHXrvm",
--Ax
[0xa6] = "||fmaddsub213pHXrvm",[0xa7] = "||fmsubadd213pHXrvm",
[0xa8] = "||fmadd213pHXrvm",[0xa9] = "||fmadd213sHXrvm",
[0xaa] = "||fmsub213pHXrvm",[0xab] = "||fmsub213sHXrvm",
[0xac] = "||fnmadd213pHXrvm",[0xad] = "||fnmadd213sHXrvm",
[0xae] = "||fnmsub213pHXrvm",[0xaf] = "||fnmsub213sHXrvm",
--Bx
[0xb6] = "||fmaddsub231pHXrvm",[0xb7] = "||fmsubadd231pHXrvm",
[0xb8] = "||fmadd231pHXrvm",[0xb9] = "||fmadd231sHXrvm",
[0xba] = "||fmsub231pHXrvm",[0xbb] = "||fmsub231sHXrvm",
[0xbc] = "||fnmadd231pHXrvm",[0xbd] = "||fnmadd231sHXrvm",
[0xbe] = "||fnmsub231pHXrvm",[0xbf] = "||fnmsub231sHXrvm",
--Dx
[0xdc] = "||aesencXrvm", [0xdd] = "||aesenclastXrvm",
[0xde] = "||aesdecXrvm", [0xdf] = "||aesdeclastXrvm",
--Fx
[0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt",
[0xf7] = "| sarxVrmv| shlxVrmv| shrxVrmv",
},
["3a"] = { -- [66] 0f 3a xx
--0x
[0x00]="||permqXrmu","||permpdXrmu","||pblenddXrvmu",nil,
"||permilpsXrmu","||permilpdXrmu","||perm2f128Xrvmu",nil,
"||roundpsXrmu","||roundpdXrmu","||roundssXrvmu","||roundsdXrvmu",
"||blendpsXrvmu","||blendpdXrvmu","||pblendwXrvmu","palignrPrvmu",
--1x
nil,nil,nil,nil,
"||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru",
"||insertf128XrvlXmu","||extractf128XlXmYru",nil,nil,
nil,nil,nil,nil,
--2x
"||pinsrbXrvVmu","||insertpsXrvmu","||pinsrXrvVmuS",nil,
--3x
[0x38] = "||inserti128Xrvmu",[0x39] = "||extracti128XlXmYru",
--4x
[0x40] = "||dppsXrvmu",
[0x41] = "||dppdXrvmu",
[0x42] = "||mpsadbwXrvmu",
[0x44] = "||pclmulqdqXrvmu",
[0x46] = "||perm2i128Xrvmu",
[0x4a] = "||blendvpsXrvmb",[0x4b] = "||blendvpdXrvmb",
[0x4c] = "||pblendvbXrvmb",
--6x
[0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu",
[0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu",
[0xdf] = "||aeskeygenassistXrmu",
--Fx
[0xf0] = "||| rorxVrmu",
},
}
-- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands).
local map_opcvm = {
[0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff",
[0xc8]="monitor",[0xc9]="mwait",
[0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave",
[0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga",
[0xf8]="swapgs",[0xf9]="rdtscp",
}
-- Map for FP opcodes. And you thought stack machines are simple?
local map_opcfp = {
-- D8-DF 00-BF: opcodes with a memory operand.
-- D8
[0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm",
"fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm",
-- DA
"fiaddDm","fimulDm","ficomDm","ficompDm",
"fisubDm","fisubrDm","fidivDm","fidivrDm",
-- DB
"fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp",
-- DC
"faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm",
-- DD
"fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm",
-- DE
"fiaddWm","fimulWm","ficomWm","ficompWm",
"fisubWm","fisubrWm","fidivWm","fidivrWm",
-- DF
"fildWm","fisttpWm","fistWm","fistpWm",
"fbld twordFmp","fildQm","fbstp twordFmp","fistpQm",
-- xx C0-FF: opcodes with a pseudo-register operand.
-- D8
"faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf",
-- D9
"fldFf","fxchFf",{"fnop"},nil,
{"fchs","fabs",nil,nil,"ftst","fxam"},
{"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"},
{"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"},
{"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"},
-- DA
"fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil,
-- DB
"fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf",
{nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil,
-- DC
"fadd toFf","fmul toFf",nil,nil,
"fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf",
-- DD
"ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil,
-- DE
"faddpFf","fmulpFf",nil,{nil,"fcompp"},
"fsubrpFf","fsubpFf","fdivrpFf","fdivpFf",
-- DF
nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil,
}
assert(map_opcfp[126] == "fcomipFf")
-- Map for opcode groups. The subkey is sp from the ModRM byte.
local map_opcgroup = {
arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" },
shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" },
testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" },
testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" },
incb = { "inc", "dec" },
incd = { "inc", "dec", "callUmp", "$call farDmp",
"jmpUmp", "$jmp farDmp", "pushUm" },
sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" },
sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt",
"smsw", nil, "lmsw", "vm*$invlpg" },
bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" },
cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil,
nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" },
pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" },
pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" },
pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" },
pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" },
fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr",
nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" },
prefetch = { "prefetch", "prefetchw" },
prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" },
}
------------------------------------------------------------------------------
-- Maps for register names.
local map_regs = {
B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
"r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" },
D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" },
Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" },
M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext!
X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" },
Y = { "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7",
"ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15" },
}
local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" }
-- Maps for size names.
local map_sz2n = {
B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16, Y = 32,
}
local map_sz2prefix = {
B = "byte", W = "word", D = "dword",
Q = "qword",
M = "qword", X = "xword", Y = "yword",
F = "dword", G = "qword", -- No need for sizes/register names for these two.
}
------------------------------------------------------------------------------
-- Output a nicely formatted line with an opcode and operands.
local function putop(ctx, text, operands)
local code, pos, hex = ctx.code, ctx.pos, ""
local hmax = ctx.hexdump
if hmax > 0 then
for i=ctx.start,pos-1 do
hex = hex..format("%02X", byte(code, i, i))
end
if #hex > hmax then hex = sub(hex, 1, hmax)..". "
else hex = hex..rep(" ", hmax-#hex+2) end
end
if operands then text = text.." "..operands end
if ctx.o16 then text = "o16 "..text; ctx.o16 = false end
if ctx.a32 then text = "a32 "..text; ctx.a32 = false end
if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end
if ctx.rex then
local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "")..
(ctx.rexx and "x" or "")..(ctx.rexb and "b" or "")..
(ctx.vexl and "l" or "")
if ctx.vexv and ctx.vexv ~= 0 then t = t.."v"..ctx.vexv end
if t ~= "" then text = ctx.rex.."."..t.." "..gsub(text, "^ ", "")
elseif ctx.rex == "vex" then text = gsub("v"..text, "^v ", "") end
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
ctx.rex = false; ctx.vexl = false; ctx.vexv = false
end
if ctx.seg then
local text2, n = gsub(text, "%[", "["..ctx.seg..":")
if n == 0 then text = ctx.seg.." "..text else text = text2 end
ctx.seg = false
end
if ctx.lock then text = "lock "..text; ctx.lock = false end
local imm = ctx.imm
if imm then
local sym = ctx.symtab[imm]
if sym then text = text.."\t->"..sym end
end
ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text))
ctx.mrm = false
ctx.vexv = false
ctx.start = pos
ctx.imm = nil
end
-- Clear all prefix flags.
local function clearprefixes(ctx)
ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
ctx.rex = false; ctx.a32 = false; ctx.vexl = false
end
-- Fallback for incomplete opcodes at the end.
local function incomplete(ctx)
ctx.pos = ctx.stop+1
clearprefixes(ctx)
return putop(ctx, "(incomplete)")
end
-- Fallback for unknown opcodes.
local function unknown(ctx)
clearprefixes(ctx)
return putop(ctx, "(unknown)")
end
-- Return an immediate of the specified size.
local function getimm(ctx, pos, n)
if pos+n-1 > ctx.stop then return incomplete(ctx) end
local code = ctx.code
if n == 1 then
local b1 = byte(code, pos, pos)
return b1
elseif n == 2 then
local b1, b2 = byte(code, pos, pos+1)
return b1+b2*256
else
local b1, b2, b3, b4 = byte(code, pos, pos+3)
local imm = b1+b2*256+b3*65536+b4*16777216
ctx.imm = imm
return imm
end
end
-- Process pattern string and generate the operands.
local function putpat(ctx, name, pat)
local operands, regs, sz, mode, sp, rm, sc, rx, sdisp
local code, pos, stop, vexl = ctx.code, ctx.pos, ctx.stop, ctx.vexl
-- Chars used: 1DFGHIMPQRSTUVWXYabcdfgijlmoprstuvwxyz
for p in gmatch(pat, ".") do
local x = nil
if p == "V" or p == "U" then
if ctx.rexw then sz = "Q"; ctx.rexw = false
elseif ctx.o16 then sz = "W"; ctx.o16 = false
elseif p == "U" and ctx.x64 then sz = "Q"
else sz = "D" end
regs = map_regs[sz]
elseif p == "T" then
if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end
regs = map_regs[sz]
elseif p == "B" then
sz = "B"
regs = ctx.rex and map_regs.B64 or map_regs.B
elseif match(p, "[WDQMXYFG]") then
sz = p
if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end
regs = map_regs[sz]
elseif p == "P" then
sz = ctx.o16 and "X" or "M"; ctx.o16 = false
if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end
regs = map_regs[sz]
elseif p == "H" then
name = name..(ctx.rexw and "d" or "s")
ctx.rexw = false
elseif p == "S" then
name = name..lower(sz)
elseif p == "s" then
local imm = getimm(ctx, pos, 1); if not imm then return end
x = imm <= 127 and format("+0x%02x", imm)
or format("-0x%02x", 256-imm)
pos = pos+1
elseif p == "u" then
local imm = getimm(ctx, pos, 1); if not imm then return end
x = format("0x%02x", imm)
pos = pos+1
elseif p == "b" then
local imm = getimm(ctx, pos, 1); if not imm then return end
x = regs[imm/16+1]
pos = pos+1
elseif p == "w" then
local imm = getimm(ctx, pos, 2); if not imm then return end
x = format("0x%x", imm)
pos = pos+2
elseif p == "o" then -- [offset]
if ctx.x64 then
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
x = format("[0x%08x%08x]", imm2, imm1)
pos = pos+8
else
local imm = getimm(ctx, pos, 4); if not imm then return end
x = format("[0x%08x]", imm)
pos = pos+4
end
elseif p == "i" or p == "I" then
local n = map_sz2n[sz]
if n == 8 and ctx.x64 and p == "I" then
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
x = format("0x%08x%08x", imm2, imm1)
else
if n == 8 then n = 4 end
local imm = getimm(ctx, pos, n); if not imm then return end
if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then
imm = (0xffffffff+1)-imm
x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm)
else
x = format(imm > 65535 and "0x%08x" or "0x%x", imm)
end
end
pos = pos+n
elseif p == "j" then
local n = map_sz2n[sz]
if n == 8 then n = 4 end
local imm = getimm(ctx, pos, n); if not imm then return end
if sz == "B" and imm > 127 then imm = imm-256
elseif imm > 2147483647 then imm = imm-4294967296 end
pos = pos+n
imm = imm + pos + ctx.addr
if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end
ctx.imm = imm
if sz == "W" then
x = format("word 0x%04x", imm%65536)
elseif ctx.x64 then
local lo = imm % 0x1000000
x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo)
else
x = "0x"..tohex(imm)
end
elseif p == "R" then
local r = byte(code, pos-1, pos-1)%8
if ctx.rexb then r = r + 8; ctx.rexb = false end
x = regs[r+1]
elseif p == "a" then x = regs[1]
elseif p == "c" then x = "cl"
elseif p == "d" then x = "dx"
elseif p == "1" then x = "1"
else
if not mode then
mode = ctx.mrm
if not mode then
if pos > stop then return incomplete(ctx) end
mode = byte(code, pos, pos)
pos = pos+1
end
rm = mode%8; mode = (mode-rm)/8
sp = mode%8; mode = (mode-sp)/8
sdisp = ""
if mode < 3 then
if rm == 4 then
if pos > stop then return incomplete(ctx) end
sc = byte(code, pos, pos)
pos = pos+1
rm = sc%8; sc = (sc-rm)/8
rx = sc%8; sc = (sc-rx)/8
if ctx.rexx then rx = rx + 8; ctx.rexx = false end
if rx == 4 then rx = nil end
end
if mode > 0 or rm == 5 then
local dsz = mode
if dsz ~= 1 then dsz = 4 end
local disp = getimm(ctx, pos, dsz); if not disp then return end
if mode == 0 then rm = nil end
if rm or rx or (not sc and ctx.x64 and not ctx.a32) then
if dsz == 1 and disp > 127 then
sdisp = format("-0x%x", 256-disp)
elseif disp >= 0 and disp <= 0x7fffffff then
sdisp = format("+0x%x", disp)
else
sdisp = format("-0x%x", (0xffffffff+1)-disp)
end
else
sdisp = format(ctx.x64 and not ctx.a32 and
not (disp >= 0 and disp <= 0x7fffffff)
and "0xffffffff%08x" or "0x%08x", disp)
end
pos = pos+dsz
end
end
if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end
if ctx.rexr then sp = sp + 8; ctx.rexr = false end
end
if p == "m" then
if mode == 3 then x = regs[rm+1]
else
local aregs = ctx.a32 and map_regs.D or ctx.aregs
local srm, srx = "", ""
if rm then srm = aregs[rm+1]
elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end
ctx.a32 = false
if rx then
if rm then srm = srm.."+" end
srx = aregs[rx+1]
if sc > 0 then srx = srx.."*"..(2^sc) end
end
x = format("[%s%s%s]", srm, srx, sdisp)
end
if mode < 3 and
(not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck.
x = map_sz2prefix[sz].." "..x
end
elseif p == "r" then x = regs[sp+1]
elseif p == "g" then x = map_segregs[sp+1]
elseif p == "p" then -- Suppress prefix.
elseif p == "f" then x = "st"..rm
elseif p == "x" then
if sp == 0 and ctx.lock and not ctx.x64 then
x = "CR8"; ctx.lock = false
else
x = "CR"..sp
end
elseif p == "v" then
if ctx.vexv then
x = regs[ctx.vexv+1]; ctx.vexv = false
end
elseif p == "y" then x = "DR"..sp
elseif p == "z" then x = "TR"..sp
elseif p == "l" then vexl = false
elseif p == "t" then
else
error("bad pattern `"..pat.."'")
end
end
if x then operands = operands and operands..", "..x or x end
end
ctx.pos = pos
return putop(ctx, name, operands)
end
-- Forward declaration.
local map_act
-- Fetch and cache MRM byte.
local function getmrm(ctx)
local mrm = ctx.mrm
if not mrm then
local pos = ctx.pos
if pos > ctx.stop then return nil end
mrm = byte(ctx.code, pos, pos)
ctx.pos = pos+1
ctx.mrm = mrm
end
return mrm
end
-- Dispatch to handler depending on pattern.
local function dispatch(ctx, opat, patgrp)
if not opat then return unknown(ctx) end
if match(opat, "%|") then -- MMX/SSE variants depending on prefix.
local p
if ctx.rep then
p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)"
ctx.rep = false
elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false
else p = "^[^%|]*" end
opat = match(opat, p)
if not opat then return unknown(ctx) end
-- ctx.rep = false; ctx.o16 = false
--XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi]
--XXX remove in branches?
end
if match(opat, "%$") then -- reg$mem variants.
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)")
if opat == "" then return unknown(ctx) end
end
if opat == "" then return unknown(ctx) end
local name, pat = match(opat, "^([a-z0-9 ]*)(.*)")
if pat == "" and patgrp then pat = patgrp end
return map_act[sub(pat, 1, 1)](ctx, name, pat)
end
-- Get a pattern from an opcode map and dispatch to handler.
local function dispatchmap(ctx, opcmap)
local pos = ctx.pos
local opat = opcmap[byte(ctx.code, pos, pos)]
pos = pos + 1
ctx.pos = pos
return dispatch(ctx, opat)
end
-- Map for action codes. The key is the first char after the name.
map_act = {
-- Simple opcodes without operands.
[""] = function(ctx, name, pat)
return putop(ctx, name)
end,
-- Operand size chars fall right through.
B = putpat, W = putpat, D = putpat, Q = putpat,
V = putpat, U = putpat, T = putpat,
M = putpat, X = putpat, P = putpat,
F = putpat, G = putpat, Y = putpat,
H = putpat,
-- Collect prefixes.
[":"] = function(ctx, name, pat)
ctx[pat == ":" and name or sub(pat, 2)] = name
if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes.
end,
-- Chain to special handler specified by name.
["*"] = function(ctx, name, pat)
return map_act[name](ctx, name, sub(pat, 2))
end,
-- Use named subtable for opcode group.
["!"] = function(ctx, name, pat)
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2))
end,
-- o16,o32[,o64] variants.
sz = function(ctx, name, pat)
if ctx.o16 then ctx.o16 = false
else
pat = match(pat, ",(.*)")
if ctx.rexw then
local p = match(pat, ",(.*)")
if p then pat = p; ctx.rexw = false end
end
end
pat = match(pat, "^[^,]*")
return dispatch(ctx, pat)
end,
-- Two-byte opcode dispatch.
opc2 = function(ctx, name, pat)
return dispatchmap(ctx, map_opc2)
end,
-- Three-byte opcode dispatch.
opc3 = function(ctx, name, pat)
return dispatchmap(ctx, map_opc3[pat])
end,
-- VMX/SVM dispatch.
vm = function(ctx, name, pat)
return dispatch(ctx, map_opcvm[ctx.mrm])
end,
-- Floating point opcode dispatch.
fp = function(ctx, name, pat)
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
local rm = mrm%8
local idx = pat*8 + ((mrm-rm)/8)%8
if mrm >= 192 then idx = idx + 64 end
local opat = map_opcfp[idx]
if type(opat) == "table" then opat = opat[rm+1] end
return dispatch(ctx, opat)
end,
-- REX prefix.
rex = function(ctx, name, pat)
if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed.
for p in gmatch(pat, ".") do ctx["rex"..p] = true end
ctx.rex = "rex"
end,
-- VEX prefix.
vex = function(ctx, name, pat)
if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed.
ctx.rex = "vex"
local pos = ctx.pos
if ctx.mrm then
ctx.mrm = nil
pos = pos-1
end
local b = byte(ctx.code, pos, pos)
if not b then return incomplete(ctx) end
pos = pos+1
if b < 128 then ctx.rexr = true end
local m = 1
if pat == "3" then
m = b%32; b = (b-m)/32
local nb = b%2; b = (b-nb)/2
if nb == 0 then ctx.rexb = true end
local nx = b%2
if nx == 0 then ctx.rexx = true end
b = byte(ctx.code, pos, pos)
if not b then return incomplete(ctx) end
pos = pos+1
if b >= 128 then ctx.rexw = true end
end
ctx.pos = pos
local map
if m == 1 then map = map_opc2
elseif m == 2 then map = map_opc3["38"]
elseif m == 3 then map = map_opc3["3a"]
else return unknown(ctx) end
local p = b%4; b = (b-p)/4
if p == 1 then ctx.o16 = "o16"
elseif p == 2 then ctx.rep = "rep"
elseif p == 3 then ctx.rep = "repne" end
local l = b%2; b = (b-l)/2
if l ~= 0 then ctx.vexl = true end
ctx.vexv = (-1-b)%16
return dispatchmap(ctx, map)
end,
-- Special case for nop with REX prefix.
nop = function(ctx, name, pat)
return dispatch(ctx, ctx.rex and pat or "nop")
end,
-- Special case for 0F 77.
emms = function(ctx, name, pat)
if ctx.rex ~= "vex" then
return putop(ctx, "emms")
elseif ctx.vexl then
ctx.vexl = false
return putop(ctx, "zeroall")
else
return putop(ctx, "zeroupper")
end
end,
}
------------------------------------------------------------------------------
-- Disassemble a block of code.
local function disass_block(ctx, ofs, len)
if not ofs then ofs = 0 end
local stop = len and ofs+len or #ctx.code
ofs = ofs + 1
ctx.start = ofs
ctx.pos = ofs
ctx.stop = stop
ctx.imm = nil
ctx.mrm = false
clearprefixes(ctx)
while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end
if ctx.pos ~= ctx.start then incomplete(ctx) end
end
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
local function create(code, addr, out)
local ctx = {}
ctx.code = code
ctx.addr = (addr or 0) - 1
ctx.out = out or io.write
ctx.symtab = {}
ctx.disass = disass_block
ctx.hexdump = 16
ctx.x64 = false
ctx.map1 = map_opc1_32
ctx.aregs = map_regs.D
return ctx
end
local function create64(code, addr, out)
local ctx = create(code, addr, out)
ctx.x64 = true
ctx.map1 = map_opc1_64
ctx.aregs = map_regs.Q
return ctx
end
-- Simple API: disassemble code (a string) at address and output via out.
local function disass(code, addr, out)
create(code, addr, out):disass()
end
local function disass64(code, addr, out)
create64(code, addr, out):disass()
end
-- Return register name for RID.
local function regname(r)
if r < 8 then return map_regs.D[r+1] end
return map_regs.X[r-7]
end
local function regname64(r)
if r < 16 then return map_regs.Q[r+1] end
return map_regs.X[r-15]
end
-- Public module functions.
return {
create = create,
create64 = create64,
disass = disass,
disass64 = disass64,
regname = regname,
regname64 = regname64
}

View file

@ -0,0 +1,725 @@
----------------------------------------------------------------------------
-- LuaJIT compiler dump module.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module can be used to debug the JIT compiler itself. It dumps the
-- code representations and structures used in various compiler stages.
--
-- Example usage:
--
-- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)"
-- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R
-- luajit -jdump=is myapp.lua | less -R
-- luajit -jdump=-b myapp.lua
-- luajit -jdump=+aH,myapp.html myapp.lua
-- luajit -jdump=ixT,myapp.dump myapp.lua
--
-- The first argument specifies the dump mode. The second argument gives
-- the output file name. Default output is to stdout, unless the environment
-- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the
-- module is started.
--
-- Different features can be turned on or off with the dump mode. If the
-- mode starts with a '+', the following features are added to the default
-- set of features; a '-' removes them. Otherwise the features are replaced.
--
-- The following dump features are available (* marks the default):
--
-- * t Print a line for each started, ended or aborted trace (see also -jv).
-- * b Dump the traced bytecode.
-- * i Dump the IR (intermediate representation).
-- r Augment the IR with register/stack slots.
-- s Dump the snapshot map.
-- * m Dump the generated machine code.
-- x Print each taken trace exit.
-- X Print each taken trace exit and the contents of all registers.
-- a Print the IR of aborted traces, too.
--
-- The output format can be set with the following characters:
--
-- T Plain text output.
-- A ANSI-colored text output
-- H Colorized HTML + CSS output.
--
-- The default output format is plain text. It's set to ANSI-colored text
-- if the COLORTERM variable is set. Note: this is independent of any output
-- redirection, which is actually considered a feature.
--
-- You probably want to use less -R to enjoy viewing ANSI-colored text from
-- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R"
--
------------------------------------------------------------------------------
-- Cache some library functions and objects.
local jit = require("jit")
local jutil = require("jit.util")
local vmdef = require("jit.vmdef")
local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc
local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek
local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap
local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr
local bit = require("bit")
local band, shr, tohex = bit.band, bit.rshift, bit.tohex
local sub, gsub, format = string.sub, string.gsub, string.format
local byte, rep = string.byte, string.rep
local type, tostring = type, tostring
local stdout, stderr = io.stdout, io.stderr
-- Load other modules on-demand.
local bcline, disass
-- Active flag, output file handle and dump mode.
local active, out, dumpmode
------------------------------------------------------------------------------
local symtabmt = { __index = false }
local symtab = {}
local nexitsym = 0
-- Fill nested symbol table with per-trace exit stub addresses.
local function fillsymtab_tr(tr, nexit)
local t = {}
symtabmt.__index = t
if jit.arch:sub(1, 4) == "mips" then
t[traceexitstub(tr, 0)] = "exit"
return
end
for i=0,nexit-1 do
local addr = traceexitstub(tr, i)
if addr < 0 then addr = addr + 2^32 end
t[addr] = tostring(i)
end
local addr = traceexitstub(tr, nexit)
if addr then t[addr] = "stack_check" end
end
-- Fill symbol table with trace exit stub addresses.
local function fillsymtab(tr, nexit)
local t = symtab
if nexitsym == 0 then
local maskaddr = jit.arch == "arm" and -2
local ircall = vmdef.ircall
for i=0,#ircall do
local addr = ircalladdr(i)
if addr ~= 0 then
if maskaddr then addr = band(addr, maskaddr) end
if addr < 0 then addr = addr + 2^32 end
t[addr] = ircall[i]
end
end
end
if nexitsym == 1000000 then -- Per-trace exit stubs.
fillsymtab_tr(tr, nexit)
elseif nexit > nexitsym then -- Shared exit stubs.
for i=nexitsym,nexit-1 do
local addr = traceexitstub(i)
if addr == nil then -- Fall back to per-trace exit stubs.
fillsymtab_tr(tr, nexit)
setmetatable(symtab, symtabmt)
nexit = 1000000
break
end
if addr < 0 then addr = addr + 2^32 end
t[addr] = tostring(i)
end
nexitsym = nexit
end
return t
end
local function dumpwrite(s)
out:write(s)
end
-- Disassemble machine code.
local function dump_mcode(tr)
local info = traceinfo(tr)
if not info then return end
local mcode, addr, loop = tracemc(tr)
if not mcode then return end
if not disass then disass = require("jit.dis_"..jit.arch) end
if addr < 0 then addr = addr + 2^32 end
out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
local ctx = disass.create(mcode, addr, dumpwrite)
ctx.hexdump = 0
ctx.symtab = fillsymtab(tr, info.nexit)
if loop ~= 0 then
symtab[addr+loop] = "LOOP"
ctx:disass(0, loop)
out:write("->LOOP:\n")
ctx:disass(loop, #mcode-loop)
symtab[addr+loop] = nil
else
ctx:disass(0, #mcode)
end
end
------------------------------------------------------------------------------
local irtype_text = {
[0] = "nil",
"fal",
"tru",
"lud",
"str",
"p32",
"thr",
"pro",
"fun",
"p64",
"cdt",
"tab",
"udt",
"flt",
"num",
"i8 ",
"u8 ",
"i16",
"u16",
"int",
"u32",
"i64",
"u64",
"sfp",
}
local colortype_ansi = {
[0] = "%s",
"%s",
"%s",
"\027[36m%s\027[m",
"\027[32m%s\027[m",
"%s",
"\027[1m%s\027[m",
"%s",
"\027[1m%s\027[m",
"%s",
"\027[33m%s\027[m",
"\027[31m%s\027[m",
"\027[36m%s\027[m",
"\027[34m%s\027[m",
"\027[34m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
}
local function colorize_text(s)
return s
end
local function colorize_ansi(s, t, extra)
local out = format(colortype_ansi[t], s)
if extra then out = "\027[3m"..out end
return out
end
local irtype_ansi = setmetatable({},
{ __index = function(tab, t)
local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
local html_escape = { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;", }
local function colorize_html(s, t, extra)
s = gsub(s, "[<>&]", html_escape)
return format('<span class="irt_%s%s">%s</span>',
irtype_text[t], extra and " irt_extra" or "", s)
end
local irtype_html = setmetatable({},
{ __index = function(tab, t)
local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
local header_html = [[
<style type="text/css">
background { background: #ffffff; color: #000000; }
pre.ljdump {
font-size: 10pt;
background: #f0f4ff;
color: #000000;
border: 1px solid #bfcfff;
padding: 0.5em;
margin-left: 2em;
margin-right: 2em;
}
span.irt_str { color: #00a000; }
span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
span.irt_tab { color: #c00000; }
span.irt_udt, span.irt_lud { color: #00c0c0; }
span.irt_num { color: #4040c0; }
span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
span.irt_extra { font-style: italic; }
</style>
]]
local colorize, irtype
-- Lookup tables to convert some literals into names.
local litname = {
["SLOAD "] = setmetatable({}, { __index = function(t, mode)
local s = ""
if band(mode, 1) ~= 0 then s = s.."P" end
if band(mode, 2) ~= 0 then s = s.."F" end
if band(mode, 4) ~= 0 then s = s.."T" end
if band(mode, 8) ~= 0 then s = s.."C" end
if band(mode, 16) ~= 0 then s = s.."R" end
if band(mode, 32) ~= 0 then s = s.."I" end
if band(mode, 64) ~= 0 then s = s.."K" end
t[mode] = s
return s
end}),
["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
["CONV "] = setmetatable({}, { __index = function(t, mode)
local s = irtype[band(mode, 31)]
s = irtype[band(shr(mode, 5), 31)].."."..s
if band(mode, 0x800) ~= 0 then s = s.." sext" end
local c = shr(mode, 12)
if c == 1 then s = s.." none"
elseif c == 2 then s = s.." index"
elseif c == 3 then s = s.." check" end
t[mode] = s
return s
end}),
["FLOAD "] = vmdef.irfield,
["FREF "] = vmdef.irfield,
["FPMATH"] = vmdef.irfpm,
["TMPREF"] = { [0] = "", "IN", "OUT", "INOUT", "", "", "OUT2", "INOUT2" },
["BUFHDR"] = { [0] = "RESET", "APPEND", "WRITE" },
["TOSTR "] = { [0] = "INT", "NUM", "CHAR" },
}
local function ctlsub(c)
if c == "\n" then return "\\n"
elseif c == "\r" then return "\\r"
elseif c == "\t" then return "\\t"
else return format("\\%03d", byte(c))
end
end
local function fmtfunc(func, pc)
local fi = funcinfo(func, pc)
if fi.loc then
return fi.loc
elseif fi.ffid then
return vmdef.ffnames[fi.ffid]
elseif fi.addr then
return format("C:%x", fi.addr)
else
return "(?)"
end
end
local function formatk(tr, idx, sn)
local k, t, slot = tracek(tr, idx)
local tn = type(k)
local s
if tn == "number" then
if t < 12 then
s = k == 0 and "NULL" or format("[0x%08x]", k)
elseif band(sn or 0, 0x30000) ~= 0 then
s = band(sn, 0x20000) ~= 0 and "contpc" or "ftsz"
elseif k == 2^52+2^51 then
s = "bias"
else
s = format(0 < k and k < 0x1p-1026 and "%+a" or "%+.14g", k)
end
elseif tn == "string" then
s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
elseif tn == "function" then
s = fmtfunc(k)
elseif tn == "table" then
s = format("{%p}", k)
elseif tn == "userdata" then
if t == 12 then
s = format("userdata:%p", k)
else
s = format("[%p]", k)
if s == "[NULL]" then s = "NULL" end
end
elseif t == 21 then -- int64_t
s = sub(tostring(k), 1, -3)
if sub(s, 1, 1) ~= "-" then s = "+"..s end
elseif sn == 0x1057fff then -- SNAP(1, SNAP_FRAME | SNAP_NORESTORE, REF_NIL)
return "----" -- Special case for LJ_FR2 slot 1.
else
s = tostring(k) -- For primitives.
end
s = colorize(format("%-4s", s), t, band(sn or 0, 0x100000) ~= 0)
if slot then
s = format("%s @%d", s, slot)
end
return s
end
local function printsnap(tr, snap)
local n = 2
for s=0,snap[1]-1 do
local sn = snap[n]
if shr(sn, 24) == s then
n = n + 1
local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
if ref < 0 then
out:write(formatk(tr, ref, sn))
elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
else
local m, ot, op1, op2 = traceir(tr, ref)
out:write(colorize(format("%04d", ref), band(ot, 31), band(sn, 0x100000) ~= 0))
end
out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
else
out:write("---- ")
end
end
out:write("]\n")
end
-- Dump snapshots (not interleaved with IR).
local function dump_snap(tr)
out:write("---- TRACE ", tr, " snapshots\n")
for i=0,1000000000 do
local snap = tracesnap(tr, i)
if not snap then break end
out:write(format("#%-3d %04d [ ", i, snap[0]))
printsnap(tr, snap)
end
end
-- Return a register name or stack slot for a rid/sp location.
local function ridsp_name(ridsp, ins)
if not disass then disass = require("jit.dis_"..jit.arch) end
local rid, slot = band(ridsp, 0xff), shr(ridsp, 8)
if rid == 253 or rid == 254 then
return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot)
end
if ridsp > 255 then return format("[%x]", slot*4) end
if rid < 128 then return disass.regname(rid) end
return ""
end
-- Dump CALL* function ref and return optional ctype.
local function dumpcallfunc(tr, ins)
local ctype
if ins > 0 then
local m, ot, op1, op2 = traceir(tr, ins)
if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
ins = op1
ctype = formatk(tr, op2)
end
end
if ins < 0 then
out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
else
out:write(format("%04d (", ins))
end
return ctype
end
-- Recursively gather CALL* args and dump them.
local function dumpcallargs(tr, ins)
if ins < 0 then
out:write(formatk(tr, ins))
else
local m, ot, op1, op2 = traceir(tr, ins)
local oidx = 6*shr(ot, 8)
local op = sub(vmdef.irnames, oidx+1, oidx+6)
if op == "CARG " then
dumpcallargs(tr, op1)
if op2 < 0 then
out:write(" ", formatk(tr, op2))
else
out:write(" ", format("%04d", op2))
end
else
out:write(format("%04d", ins))
end
end
end
-- Dump IR and interleaved snapshots.
local function dump_ir(tr, dumpsnap, dumpreg)
local info = traceinfo(tr)
if not info then return end
local nins = info.nins
out:write("---- TRACE ", tr, " IR\n")
local irnames = vmdef.irnames
local snapref = 65536
local snap, snapno
if dumpsnap then
snap = tracesnap(tr, 0)
snapref = snap[0]
snapno = 0
end
for ins=1,nins do
if ins >= snapref then
if dumpreg then
out:write(format(".... SNAP #%-3d [ ", snapno))
else
out:write(format(".... SNAP #%-3d [ ", snapno))
end
printsnap(tr, snap)
snapno = snapno + 1
snap = tracesnap(tr, snapno)
snapref = snap and snap[0] or 65536
end
local m, ot, op1, op2, ridsp = traceir(tr, ins)
local oidx, t = 6*shr(ot, 8), band(ot, 31)
local op = sub(irnames, oidx+1, oidx+6)
if op == "LOOP " then
if dumpreg then
out:write(format("%04d ------------ LOOP ------------\n", ins))
else
out:write(format("%04d ------ LOOP ------------\n", ins))
end
elseif op ~= "NOP " and op ~= "CARG " and
(dumpreg or op ~= "RENAME") then
local rid = band(ridsp, 255)
if dumpreg then
out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins)))
else
out:write(format("%04d ", ins))
end
out:write(format("%s%s %s %s ",
(rid == 254 or rid == 253) and "}" or
(band(ot, 128) == 0 and " " or ">"),
band(ot, 64) == 0 and " " or "+",
irtype[t], op))
local m1, m2 = band(m, 3), band(m, 3*4)
if sub(op, 1, 4) == "CALL" then
local ctype
if m2 == 1*4 then -- op2 == IRMlit
out:write(format("%-10s (", vmdef.ircall[op2]))
else
ctype = dumpcallfunc(tr, op2)
end
if op1 ~= -1 then dumpcallargs(tr, op1) end
out:write(")")
if ctype then out:write(" ctype ", ctype) end
elseif op == "CNEW " and op2 == -1 then
out:write(formatk(tr, op1))
elseif m1 ~= 3 then -- op1 != IRMnone
if op1 < 0 then
out:write(formatk(tr, op1))
else
out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
end
if m2 ~= 3*4 then -- op2 != IRMnone
if m2 == 1*4 then -- op2 == IRMlit
local litn = litname[op]
if litn and litn[op2] then
out:write(" ", litn[op2])
elseif op == "UREFO " or op == "UREFC " then
out:write(format(" #%-3d", shr(op2, 8)))
else
out:write(format(" #%-3d", op2))
end
elseif op2 < 0 then
out:write(" ", formatk(tr, op2))
else
out:write(format(" %04d", op2))
end
end
end
out:write("\n")
end
end
if snap then
if dumpreg then
out:write(format(".... SNAP #%-3d [ ", snapno))
else
out:write(format(".... SNAP #%-3d [ ", snapno))
end
printsnap(tr, snap)
end
end
------------------------------------------------------------------------------
local recprefix = ""
local recdepth = 0
-- Format trace error message.
local function fmterr(err, info)
if type(err) == "number" then
if type(info) == "function" then info = fmtfunc(info) end
err = format(vmdef.traceerr[err], info)
end
return err
end
-- Dump trace states.
local function dump_trace(what, tr, func, pc, otr, oex)
if what == "stop" or (what == "abort" and dumpmode.a) then
if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
elseif dumpmode.s then dump_snap(tr) end
if dumpmode.m then dump_mcode(tr) end
end
if what == "start" then
if dumpmode.H then out:write('<pre class="ljdump">\n') end
out:write("---- TRACE ", tr, " ", what)
if otr then out:write(" ", otr, "/", oex == -1 and "stitch" or oex) end
out:write(" ", fmtfunc(func, pc), "\n")
elseif what == "stop" or what == "abort" then
out:write("---- TRACE ", tr, " ", what)
if what == "abort" then
out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
else
local info = traceinfo(tr)
local link, ltype = info.link, info.linktype
if link == tr or link == 0 then
out:write(" -> ", ltype, "\n")
elseif ltype == "root" then
out:write(" -> ", link, "\n")
else
out:write(" -> ", link, " ", ltype, "\n")
end
end
if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
else
if what == "flush" then symtab, nexitsym = {}, 0 end
out:write("---- TRACE ", what, "\n\n")
end
out:flush()
end
-- Dump recorded bytecode.
local function dump_record(tr, func, pc, depth)
if depth ~= recdepth then
recdepth = depth
recprefix = rep(" .", depth)
end
local line
if pc >= 0 then
line = bcline(func, pc, recprefix)
if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
else
line = "0000 "..recprefix.." FUNCC \n"
end
if pc <= 0 then
out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
else
out:write(line)
end
if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
end
end
------------------------------------------------------------------------------
local gpr64 = jit.arch:match("64")
local fprmips32 = jit.arch == "mips" or jit.arch == "mipsel"
-- Dump taken trace exits.
local function dump_texit(tr, ex, ngpr, nfpr, ...)
out:write("---- TRACE ", tr, " exit ", ex, "\n")
if dumpmode.X then
local regs = {...}
if gpr64 then
for i=1,ngpr do
out:write(format(" %016x", regs[i]))
if i % 4 == 0 then out:write("\n") end
end
else
for i=1,ngpr do
out:write(" ", tohex(regs[i]))
if i % 8 == 0 then out:write("\n") end
end
end
if fprmips32 then
for i=1,nfpr,2 do
out:write(format(" %+17.14g", regs[ngpr+i]))
if i % 8 == 7 then out:write("\n") end
end
else
for i=1,nfpr do
out:write(format(" %+17.14g", regs[ngpr+i]))
if i % 4 == 0 then out:write("\n") end
end
end
end
end
------------------------------------------------------------------------------
-- Detach dump handlers.
local function dumpoff()
if active then
active = false
jit.attach(dump_texit)
jit.attach(dump_record)
jit.attach(dump_trace)
if out and out ~= stdout and out ~= stderr then out:close() end
out = nil
end
end
-- Open the output file and attach dump handlers.
local function dumpon(opt, outfile)
if active then dumpoff() end
local term = os.getenv("TERM")
local colormode = (term and term:match("color") or os.getenv("COLORTERM")) and "A" or "T"
if opt then
opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
end
local m = { t=true, b=true, i=true, m=true, }
if opt and opt ~= "" then
local o = sub(opt, 1, 1)
if o ~= "+" and o ~= "-" then m = {} end
for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
end
dumpmode = m
if m.t or m.b or m.i or m.s or m.m then
jit.attach(dump_trace, "trace")
end
if m.b then
jit.attach(dump_record, "record")
if not bcline then bcline = require("jit.bc").line end
end
if m.x or m.X then
jit.attach(dump_texit, "texit")
end
if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
if outfile then
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
else
out = stdout
end
m[colormode] = true
if colormode == "A" then
colorize = colorize_ansi
irtype = irtype_ansi
elseif colormode == "H" then
colorize = colorize_html
irtype = irtype_html
out:write(header_html)
else
colorize = colorize_text
irtype = irtype_text
end
active = true
end
-- Public module functions.
return {
on = dumpon,
off = dumpoff,
start = dumpon -- For -j command line option.
}

View file

@ -0,0 +1,311 @@
----------------------------------------------------------------------------
-- LuaJIT profiler.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module is a simple command line interface to the built-in
-- low-overhead profiler of LuaJIT.
--
-- The lower-level API of the profiler is accessible via the "jit.profile"
-- module or the luaJIT_profile_* C API.
--
-- Example usage:
--
-- luajit -jp myapp.lua
-- luajit -jp=s myapp.lua
-- luajit -jp=-s myapp.lua
-- luajit -jp=vl myapp.lua
-- luajit -jp=G,profile.txt myapp.lua
--
-- The following dump features are available:
--
-- f Stack dump: function name, otherwise module:line. Default mode.
-- F Stack dump: ditto, but always prepend module.
-- l Stack dump: module:line.
-- <number> stack dump depth (callee < caller). Default: 1.
-- -<number> Inverse stack dump depth (caller > callee).
-- s Split stack dump after first stack level. Implies abs(depth) >= 2.
-- p Show full path for module names.
-- v Show VM states. Can be combined with stack dumps, e.g. vf or fv.
-- z Show zones. Can be combined with stack dumps, e.g. zf or fz.
-- r Show raw sample counts. Default: show percentages.
-- a Annotate excerpts from source code files.
-- A Annotate complete source code files.
-- G Produce raw output suitable for graphical tools (e.g. flame graphs).
-- m<number> Minimum sample percentage to be shown. Default: 3.
-- i<number> Sampling interval in milliseconds. Default: 10.
--
----------------------------------------------------------------------------
-- Cache some library functions and objects.
local jit = require("jit")
local profile = require("jit.profile")
local vmdef = require("jit.vmdef")
local math = math
local pairs, ipairs, tonumber, floor = pairs, ipairs, tonumber, math.floor
local sort, format = table.sort, string.format
local stdout = io.stdout
local zone -- Load jit.zone module on demand.
-- Output file handle.
local out
------------------------------------------------------------------------------
local prof_ud
local prof_states, prof_split, prof_min, prof_raw, prof_fmt, prof_depth
local prof_ann, prof_count1, prof_count2, prof_samples
local map_vmmode = {
N = "Compiled",
I = "Interpreted",
C = "C code",
G = "Garbage Collector",
J = "JIT Compiler",
}
-- Profiler callback.
local function prof_cb(th, samples, vmmode)
prof_samples = prof_samples + samples
local key_stack, key_stack2, key_state
-- Collect keys for sample.
if prof_states then
if prof_states == "v" then
key_state = map_vmmode[vmmode] or vmmode
else
key_state = zone:get() or "(none)"
end
end
if prof_fmt then
key_stack = profile.dumpstack(th, prof_fmt, prof_depth)
key_stack = key_stack:gsub("%[builtin#(%d+)%]", function(x)
return vmdef.ffnames[tonumber(x)]
end)
if prof_split == 2 then
local k1, k2 = key_stack:match("(.-) [<>] (.*)")
if k2 then key_stack, key_stack2 = k1, k2 end
elseif prof_split == 3 then
key_stack2 = profile.dumpstack(th, "l", 1)
end
end
-- Order keys.
local k1, k2
if prof_split == 1 then
if key_state then
k1 = key_state
if key_stack then k2 = key_stack end
end
elseif key_stack then
k1 = key_stack
if key_stack2 then k2 = key_stack2 elseif key_state then k2 = key_state end
end
-- Coalesce samples in one or two levels.
if k1 then
local t1 = prof_count1
t1[k1] = (t1[k1] or 0) + samples
if k2 then
local t2 = prof_count2
local t3 = t2[k1]
if not t3 then t3 = {}; t2[k1] = t3 end
t3[k2] = (t3[k2] or 0) + samples
end
end
end
------------------------------------------------------------------------------
-- Show top N list.
local function prof_top(count1, count2, samples, indent)
local t, n = {}, 0
for k in pairs(count1) do
n = n + 1
t[n] = k
end
sort(t, function(a, b) return count1[a] > count1[b] end)
for i=1,n do
local k = t[i]
local v = count1[k]
local pct = floor(v*100/samples + 0.5)
if pct < prof_min then break end
if not prof_raw then
out:write(format("%s%2d%% %s\n", indent, pct, k))
elseif prof_raw == "r" then
out:write(format("%s%5d %s\n", indent, v, k))
else
out:write(format("%s %d\n", k, v))
end
if count2 then
local r = count2[k]
if r then
prof_top(r, nil, v, (prof_split == 3 or prof_split == 1) and " -- " or
(prof_depth < 0 and " -> " or " <- "))
end
end
end
end
-- Annotate source code
local function prof_annotate(count1, samples)
local files = {}
local ms = 0
for k, v in pairs(count1) do
local pct = floor(v*100/samples + 0.5)
ms = math.max(ms, v)
if pct >= prof_min then
local file, line = k:match("^(.*):(%d+)$")
if not file then file = k; line = 0 end
local fl = files[file]
if not fl then fl = {}; files[file] = fl; files[#files+1] = file end
line = tonumber(line)
fl[line] = prof_raw and v or pct
end
end
sort(files)
local fmtv, fmtn = " %3d%% | %s\n", " | %s\n"
if prof_raw then
local n = math.max(5, math.ceil(math.log10(ms)))
fmtv = "%"..n.."d | %s\n"
fmtn = (" "):rep(n).." | %s\n"
end
local ann = prof_ann
for _, file in ipairs(files) do
local f0 = file:byte()
if f0 == 40 or f0 == 91 then
out:write(format("\n====== %s ======\n[Cannot annotate non-file]\n", file))
break
end
local fp, err = io.open(file)
if not fp then
out:write(format("====== ERROR: %s: %s\n", file, err))
break
end
out:write(format("\n====== %s ======\n", file))
local fl = files[file]
local n, show = 1, false
if ann ~= 0 then
for i=1,ann do
if fl[i] then show = true; out:write("@@ 1 @@\n"); break end
end
end
for line in fp:lines() do
if line:byte() == 27 then
out:write("[Cannot annotate bytecode file]\n")
break
end
local v = fl[n]
if ann ~= 0 then
local v2 = fl[n+ann]
if show then
if v2 then show = n+ann elseif v then show = n
elseif show+ann < n then show = false end
elseif v2 then
show = n+ann
out:write(format("@@ %d @@\n", n))
end
if not show then goto next end
end
if v then
out:write(format(fmtv, v, line))
else
out:write(format(fmtn, line))
end
::next::
n = n + 1
end
fp:close()
end
end
------------------------------------------------------------------------------
-- Finish profiling and dump result.
local function prof_finish()
if prof_ud then
profile.stop()
local samples = prof_samples
if samples == 0 then
if prof_raw ~= true then out:write("[No samples collected]\n") end
return
end
if prof_ann then
prof_annotate(prof_count1, samples)
else
prof_top(prof_count1, prof_count2, samples, "")
end
prof_count1 = nil
prof_count2 = nil
prof_ud = nil
if out ~= stdout then out:close() end
end
end
-- Start profiling.
local function prof_start(mode)
local interval = ""
mode = mode:gsub("i%d*", function(s) interval = s; return "" end)
prof_min = 3
mode = mode:gsub("m(%d+)", function(s) prof_min = tonumber(s); return "" end)
prof_depth = 1
mode = mode:gsub("%-?%d+", function(s) prof_depth = tonumber(s); return "" end)
local m = {}
for c in mode:gmatch(".") do m[c] = c end
prof_states = m.z or m.v
if prof_states == "z" then zone = require("jit.zone") end
local scope = m.l or m.f or m.F or (prof_states and "" or "f")
local flags = (m.p or "")
prof_raw = m.r
if m.s then
prof_split = 2
if prof_depth == -1 or m["-"] then prof_depth = -2
elseif prof_depth == 1 then prof_depth = 2 end
elseif mode:find("[fF].*l") then
scope = "l"
prof_split = 3
else
prof_split = (scope == "" or mode:find("[zv].*[lfF]")) and 1 or 0
end
prof_ann = m.A and 0 or (m.a and 3)
if prof_ann then
scope = "l"
prof_fmt = "pl"
prof_split = 0
prof_depth = 1
elseif m.G and scope ~= "" then
prof_fmt = flags..scope.."Z;"
prof_depth = -100
prof_raw = true
prof_min = 0
elseif scope == "" then
prof_fmt = false
else
local sc = prof_split == 3 and m.f or m.F or scope
prof_fmt = flags..sc..(prof_depth >= 0 and "Z < " or "Z > ")
end
prof_count1 = {}
prof_count2 = {}
prof_samples = 0
profile.start(scope:lower()..interval, prof_cb)
prof_ud = newproxy(true)
getmetatable(prof_ud).__gc = prof_finish
end
------------------------------------------------------------------------------
local function start(mode, outfile)
if not outfile then outfile = os.getenv("LUAJIT_PROFILEFILE") end
if outfile then
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
else
out = stdout
end
prof_start(mode or "f")
end
-- Public module functions.
return {
start = start, -- For -j command line option.
stop = prof_finish
}

View file

@ -0,0 +1,169 @@
----------------------------------------------------------------------------
-- Verbose mode of the LuaJIT compiler.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module shows verbose information about the progress of the
-- JIT compiler. It prints one line for each generated trace. This module
-- is useful to see which code has been compiled or where the compiler
-- punts and falls back to the interpreter.
--
-- Example usage:
--
-- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end"
-- luajit -jv=myapp.out myapp.lua
--
-- Default output is to stderr. To redirect the output to a file, pass a
-- filename as an argument (use '-' for stdout) or set the environment
-- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the
-- module is started.
--
-- The output from the first example should look like this:
--
-- [TRACE 1 (command line):1 loop]
-- [TRACE 2 (1/3) (command line):1 -> 1]
--
-- The first number in each line is the internal trace number. Next are
-- the file name ('(command line)') and the line number (':1') where the
-- trace has started. Side traces also show the parent trace number and
-- the exit number where they are attached to in parentheses ('(1/3)').
-- An arrow at the end shows where the trace links to ('-> 1'), unless
-- it loops to itself.
--
-- In this case the inner loop gets hot and is traced first, generating
-- a root trace. Then the last exit from the 1st trace gets hot, too,
-- and triggers generation of the 2nd trace. The side trace follows the
-- path along the outer loop and *around* the inner loop, back to its
-- start, and then links to the 1st trace. Yes, this may seem unusual,
-- if you know how traditional compilers work. Trace compilers are full
-- of surprises like this -- have fun! :-)
--
-- Aborted traces are shown like this:
--
-- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50]
--
-- Don't worry -- trace aborts are quite common, even in programs which
-- can be fully compiled. The compiler may retry several times until it
-- finds a suitable trace.
--
-- Of course this doesn't work with features that are not-yet-implemented
-- (NYI error messages). The VM simply falls back to the interpreter. This
-- may not matter at all if the particular trace is not very high up in
-- the CPU usage profile. Oh, and the interpreter is quite fast, too.
--
-- Also check out the -jdump module, which prints all the gory details.
--
------------------------------------------------------------------------------
-- Cache some library functions and objects.
local jit = require("jit")
local jutil = require("jit.util")
local vmdef = require("jit.vmdef")
local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo
local type, format = type, string.format
local stdout, stderr = io.stdout, io.stderr
-- Active flag and output file handle.
local active, out
------------------------------------------------------------------------------
local startloc, startex
local function fmtfunc(func, pc)
local fi = funcinfo(func, pc)
if fi.loc then
return fi.loc
elseif fi.ffid then
return vmdef.ffnames[fi.ffid]
elseif fi.addr then
return format("C:%x", fi.addr)
else
return "(?)"
end
end
-- Format trace error message.
local function fmterr(err, info)
if type(err) == "number" then
if type(info) == "function" then info = fmtfunc(info) end
err = format(vmdef.traceerr[err], info)
end
return err
end
-- Dump trace states.
local function dump_trace(what, tr, func, pc, otr, oex)
if what == "start" then
startloc = fmtfunc(func, pc)
startex = otr and "("..otr.."/"..(oex == -1 and "stitch" or oex)..") " or ""
else
if what == "abort" then
local loc = fmtfunc(func, pc)
if loc ~= startloc then
out:write(format("[TRACE --- %s%s -- %s at %s]\n",
startex, startloc, fmterr(otr, oex), loc))
else
out:write(format("[TRACE --- %s%s -- %s]\n",
startex, startloc, fmterr(otr, oex)))
end
elseif what == "stop" then
local info = traceinfo(tr)
local link, ltype = info.link, info.linktype
if ltype == "interpreter" then
out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n",
tr, startex, startloc))
elseif ltype == "stitch" then
out:write(format("[TRACE %3s %s%s %s %s]\n",
tr, startex, startloc, ltype, fmtfunc(func, pc)))
elseif link == tr or link == 0 then
out:write(format("[TRACE %3s %s%s %s]\n",
tr, startex, startloc, ltype))
elseif ltype == "root" then
out:write(format("[TRACE %3s %s%s -> %d]\n",
tr, startex, startloc, link))
else
out:write(format("[TRACE %3s %s%s -> %d %s]\n",
tr, startex, startloc, link, ltype))
end
else
out:write(format("[TRACE %s]\n", what))
end
out:flush()
end
end
------------------------------------------------------------------------------
-- Detach dump handlers.
local function dumpoff()
if active then
active = false
jit.attach(dump_trace)
if out and out ~= stdout and out ~= stderr then out:close() end
out = nil
end
end
-- Open the output file and attach dump handlers.
local function dumpon(outfile)
if active then dumpoff() end
if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end
if outfile then
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
else
out = stderr
end
jit.attach(dump_trace, "trace")
active = true
end
-- Public module functions.
return {
on = dumpon,
off = dumpoff,
start = dumpon -- For -j command line option.
}

View file

@ -0,0 +1,396 @@
-- This is a generated file. DO NOT EDIT!
assert(require("jit").version == "LuaJIT 2.1.1700008891", "LuaJIT core/library version mismatch")
return {
bcnames = "ISLT ISGE ISLE ISGT ISEQV ISNEV ISEQS ISNES ISEQN ISNEN ISEQP ISNEP ISTC ISFC IST ISF ISTYPEISNUM MOV NOT UNM LEN ADDVN SUBVN MULVN DIVVN MODVN ADDNV SUBNV MULNV DIVNV MODNV ADDVV SUBVV MULVV DIVVV MODVV POW CAT KSTR KCDATAKSHORTKNUM KPRI KNIL UGET USETV USETS USETN USETP UCLO FNEW TNEW TDUP GGET GSET TGETV TGETS TGETB TGETR TSETV TSETS TSETB TSETM TSETR CALLM CALL CALLMTCALLT ITERC ITERN VARG ISNEXTRETM RET RET0 RET1 FORI JFORI FORL IFORL JFORL ITERL IITERLJITERLLOOP ILOOP JLOOP JMP FUNCF IFUNCFJFUNCFFUNCV IFUNCVJFUNCVFUNCC FUNCCW",
irnames = "LT GE LE GT ULT UGE ULE UGT EQ NE ABC RETF NOP BASE PVAL GCSTEPHIOP LOOP USE PHI RENAMEPROF KPRI KINT KGC KPTR KKPTR KNULL KNUM KINT64KSLOT BNOT BSWAP BAND BOR BXOR BSHL BSHR BSAR BROL BROR ADD SUB MUL DIV MOD POW NEG ABS LDEXP MIN MAX FPMATHADDOV SUBOV MULOV AREF HREFK HREF NEWREFUREFO UREFC FREF TMPREFSTRREFLREF ALOAD HLOAD ULOAD FLOAD XLOAD SLOAD VLOAD ALEN ASTOREHSTOREUSTOREFSTOREXSTORESNEW XSNEW TNEW TDUP CNEW CNEWI BUFHDRBUFPUTBUFSTRTBAR OBAR XBAR CONV TOBIT TOSTR STRTO CALLN CALLA CALLL CALLS CALLXSCARG ",
irfpm = { [0]="floor", "ceil", "trunc", "sqrt", "log", "log2", "other", },
irfield = { [0]="str.len", "func.env", "func.pc", "func.ffid", "thread.env", "tab.meta", "tab.array", "tab.node", "tab.asize", "tab.hmask", "tab.nomm", "udata.meta", "udata.udtype", "udata.file", "sbuf.w", "sbuf.e", "sbuf.b", "sbuf.l", "sbuf.ref", "sbuf.r", "cdata.ctypeid", "cdata.ptr", "cdata.int", "cdata.int64", "cdata.int64_4", },
ircall = {
[0]="lj_str_cmp",
"lj_str_find",
"lj_str_new",
"lj_strscan_num",
"lj_strfmt_int",
"lj_strfmt_num",
"lj_strfmt_char",
"lj_strfmt_putint",
"lj_strfmt_putnum",
"lj_strfmt_putquoted",
"lj_strfmt_putfxint",
"lj_strfmt_putfnum_int",
"lj_strfmt_putfnum_uint",
"lj_strfmt_putfnum",
"lj_strfmt_putfstr",
"lj_strfmt_putfchar",
"lj_buf_putmem",
"lj_buf_putstr",
"lj_buf_putchar",
"lj_buf_putstr_reverse",
"lj_buf_putstr_lower",
"lj_buf_putstr_upper",
"lj_buf_putstr_rep",
"lj_buf_puttab",
"lj_bufx_set",
"lj_bufx_more",
"lj_serialize_put",
"lj_serialize_get",
"lj_serialize_encode",
"lj_serialize_decode",
"lj_buf_tostr",
"lj_tab_new_ah",
"lj_tab_new1",
"lj_tab_dup",
"lj_tab_clear",
"lj_tab_newkey",
"lj_tab_keyindex",
"lj_vm_next",
"lj_tab_len",
"lj_tab_len_hint",
"lj_gc_step_jit",
"lj_gc_barrieruv",
"lj_mem_newgco",
"lj_prng_u64d",
"lj_vm_modi",
"log10",
"exp",
"sin",
"cos",
"tan",
"asin",
"acos",
"atan",
"sinh",
"cosh",
"tanh",
"fputc",
"fwrite",
"fflush",
"lj_vm_floor",
"lj_vm_ceil",
"lj_vm_trunc",
"sqrt",
"log",
"lj_vm_log2",
"pow",
"atan2",
"ldexp",
"lj_vm_tobit",
"softfp_add",
"softfp_sub",
"softfp_mul",
"softfp_div",
"softfp_cmp",
"softfp_i2d",
"softfp_d2i",
"lj_vm_sfmin",
"lj_vm_sfmax",
"lj_vm_tointg",
"softfp_ui2d",
"softfp_f2d",
"softfp_d2ui",
"softfp_d2f",
"softfp_i2f",
"softfp_ui2f",
"softfp_f2i",
"softfp_f2ui",
"fp64_l2d",
"fp64_ul2d",
"fp64_l2f",
"fp64_ul2f",
"fp64_d2l",
"fp64_d2ul",
"fp64_f2l",
"fp64_f2ul",
"lj_carith_divi64",
"lj_carith_divu64",
"lj_carith_modi64",
"lj_carith_modu64",
"lj_carith_powi64",
"lj_carith_powu64",
"lj_cdata_newv",
"lj_cdata_setfin",
"strlen",
"memcpy",
"memset",
"lj_vm_errno",
"lj_carith_mul64",
"lj_carith_shl64",
"lj_carith_shr64",
"lj_carith_sar64",
"lj_carith_rol64",
"lj_carith_ror64",
},
traceerr = {
[0]="error thrown or hook called during recording",
"trace too short",
"trace too long",
"trace too deep",
"too many snapshots",
"blacklisted",
"retry recording",
"NYI: bytecode %d",
"leaving loop in root trace",
"inner loop in root trace",
"loop unroll limit reached",
"bad argument type",
"JIT compilation disabled for function",
"call unroll limit reached",
"down-recursion, restarting",
"NYI: unsupported variant of FastFunc %s",
"NYI: return to lower frame",
"store with nil or NaN key",
"missing metamethod",
"looping index lookup",
"NYI: mixed sparse/dense table",
"symbol not in cache",
"NYI: unsupported C type conversion",
"NYI: unsupported C function type",
"guard would always fail",
"too many PHIs",
"persistent type instability",
"failed to allocate mcode memory",
"machine code too long",
"hit mcode limit (retrying)",
"too many spill slots",
"inconsistent register allocation",
"NYI: cannot assemble IR instruction %d",
"NYI: PHI shuffling too complex",
"NYI: register coalescing too complex",
},
ffnames = {
[0]="Lua",
"C",
"assert",
"type",
"next",
"pairs",
"ipairs_aux",
"ipairs",
"getmetatable",
"setmetatable",
"getfenv",
"setfenv",
"rawget",
"rawset",
"rawequal",
"unpack",
"select",
"tonumber",
"tostring",
"error",
"pcall",
"xpcall",
"loadfile",
"load",
"loadstring",
"dofile",
"gcinfo",
"collectgarbage",
"newproxy",
"print",
"coroutine.status",
"coroutine.running",
"coroutine.isyieldable",
"coroutine.create",
"coroutine.yield",
"coroutine.resume",
"coroutine.wrap_aux",
"coroutine.wrap",
"math.abs",
"math.floor",
"math.ceil",
"math.sqrt",
"math.log10",
"math.exp",
"math.sin",
"math.cos",
"math.tan",
"math.asin",
"math.acos",
"math.atan",
"math.sinh",
"math.cosh",
"math.tanh",
"math.frexp",
"math.modf",
"math.log",
"math.atan2",
"math.pow",
"math.fmod",
"math.ldexp",
"math.min",
"math.max",
"math.random",
"math.randomseed",
"bit.tobit",
"bit.bnot",
"bit.bswap",
"bit.lshift",
"bit.rshift",
"bit.arshift",
"bit.rol",
"bit.ror",
"bit.band",
"bit.bor",
"bit.bxor",
"bit.tohex",
"string.byte",
"string.char",
"string.sub",
"string.rep",
"string.reverse",
"string.lower",
"string.upper",
"string.dump",
"string.find",
"string.match",
"string.gmatch_aux",
"string.gmatch",
"string.gsub",
"string.format",
"table.maxn",
"table.insert",
"table.concat",
"table.sort",
"table.new",
"table.clear",
"io.method.close",
"io.method.read",
"io.method.write",
"io.method.flush",
"io.method.seek",
"io.method.setvbuf",
"io.method.lines",
"io.method.__gc",
"io.method.__tostring",
"io.open",
"io.popen",
"io.tmpfile",
"io.close",
"io.read",
"io.write",
"io.flush",
"io.input",
"io.output",
"io.lines",
"io.type",
"os.execute",
"os.remove",
"os.rename",
"os.tmpname",
"os.getenv",
"os.exit",
"os.clock",
"os.date",
"os.time",
"os.difftime",
"os.setlocale",
"debug.getregistry",
"debug.getmetatable",
"debug.setmetatable",
"debug.getfenv",
"debug.setfenv",
"debug.getinfo",
"debug.getlocal",
"debug.setlocal",
"debug.getupvalue",
"debug.setupvalue",
"debug.upvalueid",
"debug.upvaluejoin",
"debug.sethook",
"debug.gethook",
"debug.debug",
"debug.traceback",
"jit.on",
"jit.off",
"jit.flush",
"jit.status",
"jit.security",
"jit.attach",
"jit.util.funcinfo",
"jit.util.funcbc",
"jit.util.funck",
"jit.util.funcuvname",
"jit.util.traceinfo",
"jit.util.traceir",
"jit.util.tracek",
"jit.util.tracesnap",
"jit.util.tracemc",
"jit.util.traceexitstub",
"jit.util.ircalladdr",
"jit.opt.start",
"jit.profile.start",
"jit.profile.stop",
"jit.profile.dumpstack",
"ffi.meta.__index",
"ffi.meta.__newindex",
"ffi.meta.__eq",
"ffi.meta.__len",
"ffi.meta.__lt",
"ffi.meta.__le",
"ffi.meta.__concat",
"ffi.meta.__call",
"ffi.meta.__add",
"ffi.meta.__sub",
"ffi.meta.__mul",
"ffi.meta.__div",
"ffi.meta.__mod",
"ffi.meta.__pow",
"ffi.meta.__unm",
"ffi.meta.__tostring",
"ffi.meta.__pairs",
"ffi.meta.__ipairs",
"ffi.clib.__index",
"ffi.clib.__newindex",
"ffi.clib.__gc",
"ffi.callback.free",
"ffi.callback.set",
"ffi.cdef",
"ffi.new",
"ffi.cast",
"ffi.typeof",
"ffi.typeinfo",
"ffi.istype",
"ffi.sizeof",
"ffi.alignof",
"ffi.offsetof",
"ffi.errno",
"ffi.string",
"ffi.copy",
"ffi.fill",
"ffi.abi",
"ffi.metatype",
"ffi.gc",
"ffi.load",
"buffer.method.free",
"buffer.method.reset",
"buffer.method.skip",
"buffer.method.set",
"buffer.method.put",
"buffer.method.putf",
"buffer.method.get",
"buffer.method.putcdata",
"buffer.method.reserve",
"buffer.method.commit",
"buffer.method.ref",
"buffer.method.encode",
"buffer.method.decode",
"buffer.method.__gc",
"buffer.method.__tostring",
"buffer.method.__len",
"buffer.new",
"buffer.encode",
"buffer.decode",
},
}

View file

@ -0,0 +1,45 @@
----------------------------------------------------------------------------
-- LuaJIT profiler zones.
--
-- Copyright (C) 2005-2023 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module implements a simple hierarchical zone model.
--
-- Example usage:
--
-- local zone = require("jit.zone")
-- zone("AI")
-- ...
-- zone("A*")
-- ...
-- print(zone:get()) --> "A*"
-- ...
-- zone()
-- ...
-- print(zone:get()) --> "AI"
-- ...
-- zone()
--
----------------------------------------------------------------------------
local remove = table.remove
return setmetatable({
flush = function(t)
for i=#t,1,-1 do t[i] = nil end
end,
get = function(t)
return t[#t]
end
}, {
__call = function(t, zone)
if zone then
t[#t+1] = zone
else
return (assert(remove(t), "empty zone stack"))
end
end
})

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-love-game">
<comment>LÖVE game</comment>
<glob pattern="*.love"/>
</mime-type>
</mime-info>

View file

@ -0,0 +1,962 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="141.73px" height="141.73px" viewBox="0 0 141.73 141.73" enable-background="new 0 0 141.73 141.73" xml:space="preserve">
<g>
<g opacity="0.3">
<path d="M117.726,21.413c-0.016-0.017-0.035-0.027-0.053-0.042C96.307,3.014,66.118-0.243,41.136,12.523
C16.168,25.282,1.956,53.733,5.382,81.306c2.392,19.268,13.129,36.165,28.466,47.021c0,0,42.277-37.776,53.504-51.181
c10.53-12.582,33.52-52.764,33.52-52.764C119.861,23.365,118.814,22.374,117.726,21.413z"/>
</g>
<g>
<path fill="#E74A99" d="M115.726,18.413c-0.016-0.017-0.035-0.027-0.053-0.042C94.307,0.014,64.118-3.243,39.136,9.523
C14.168,22.282-0.044,50.733,3.382,78.306c2.392,19.268,13.129,36.165,28.466,47.021c0,0,42.277-37.776,53.504-51.181
c10.53-12.582,33.52-52.764,33.52-52.764C117.861,20.365,116.814,19.374,115.726,18.413z"/>
</g>
</g>
<g>
<g opacity="0.3">
<path d="M33.849,128.327c7.067,5.001,15.11,8.726,23.765,10.801c27.076,6.487,56.27-5.293,71.759-28.261
c18.256-27.077,14.241-63.649-8.5-86.485c0,0-32.464,38.778-43.287,51.706C66.65,89.146,33.849,128.327,33.849,128.327z"/>
</g>
<g>
<path fill="#27AAE1" d="M31.849,125.327c7.067,5.001,15.11,8.726,23.765,10.801c27.076,6.487,56.27-5.293,71.759-28.261
c18.256-27.077,14.241-63.649-8.5-86.485c0,0-32.464,38.778-43.287,51.706C64.65,86.146,31.849,125.327,31.849,125.327z"/>
</g>
</g>
<g>
<image overflow="visible" opacity="0.3" width="354" height="324" xlink:href="data:image/png;base64,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
rkJggg==" transform="matrix(0.24 0 0 0.24 28.4971 38.3643)">
</image>
<g>
<path fill="#FFFFFF" d="M73.043,52.8c0,0-6.48-14.187-19.968-10.958c-13.486,3.23-41.652,30.792,17.853,65.083H70.88
c59.504-34.291,31.34-61.852,17.853-65.083C75.245,38.613,68.766,52.8,68.766,52.8H73.043z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 72 KiB

View file

@ -0,0 +1 @@
appimagetool.svg

View file

@ -0,0 +1,11 @@
#!/bin/sh
HERE="$(dirname "$(readlink -f "${0}")")"
export PATH="${HERE}"/usr/bin/:"${HERE}"/usr/sbin/:"${HERE}"/usr/games/:"${HERE}"/bin/:"${HERE}"/sbin/:"${PATH}"
export LD_LIBRARY_PATH="${HERE}"/usr/lib/:"${HERE}"/usr/lib/i386-linux-gnu/:"${HERE}"/usr/lib/x86_64-linux-gnu/:"${HERE}"/usr/lib32/:"${HERE}"/usr/lib64/:"${HERE}"/lib/:"${HERE}"/lib/i386-linux-gnu/:"${HERE}"/lib/x86_64-linux-gnu/:"${HERE}"/lib32/:"${HERE}"/lib64/:"${LD_LIBRARY_PATH}"
export PYTHONPATH="${HERE}"/usr/share/pyshared/:"${PYTHONPATH}"
export XDG_DATA_DIRS="${HERE}"/usr/share/:"${XDG_DATA_DIRS}"
export PERLLIB="${HERE}"/usr/share/perl5/:"${HERE}"/usr/lib/perl5/:"${PERLLIB}"
export GSETTINGS_SCHEMA_DIR="${HERE}"/usr/share/glib-2.0/schemas/:"${GSETTINGS_SCHEMA_DIR}"
export QT_PLUGIN_PATH="${HERE}"/usr/lib/qt4/plugins/:"${HERE}"/usr/lib/i386-linux-gnu/qt4/plugins/:"${HERE}"/usr/lib/x86_64-linux-gnu/qt4/plugins/:"${HERE}"/usr/lib32/qt4/plugins/:"${HERE}"/usr/lib64/qt4/plugins/:"${HERE}"/usr/lib/qt5/plugins/:"${HERE}"/usr/lib/i386-linux-gnu/qt5/plugins/:"${HERE}"/usr/lib/x86_64-linux-gnu/qt5/plugins/:"${HERE}"/usr/lib32/qt5/plugins/:"${HERE}"/usr/lib64/qt5/plugins/:"${QT_PLUGIN_PATH}"
EXEC=$(grep -e '^Exec=.*' "${HERE}"/*.desktop | head -n 1 | cut -d "=" -f 2 | cut -d " " -f 1)
exec "${EXEC}" $@

View file

@ -0,0 +1,9 @@
[Desktop Entry]
Type=Application
Name=appimagetool
Exec=appimagetool
Comment=Tool to generate AppImages from AppDirs
Icon=appimagetool
Categories=Development;
Terminal=true

View file

@ -0,0 +1,320 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48px"
height="48px"
id="svg3832"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="appimage-assistant_alt3.svg">
<defs
id="defs3834">
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3308-4-6-931-761-0"
id="linearGradient2975"
gradientUnits="userSpaceOnUse"
x1="24.3125"
y1="22.96875"
x2="24.3125"
y2="41.03125" />
<linearGradient
id="linearGradient3308-4-6-931-761-0">
<stop
id="stop2919-2"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop2921-76"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4222"
id="linearGradient2979"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,0.3704967,-0.3617496,0,33.508315,6.1670925)"
x1="7.6485429"
y1="26.437023"
x2="41.861729"
y2="26.437023" />
<linearGradient
id="linearGradient4222">
<stop
id="stop4224"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop4226"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3308-4-6-931-761"
id="linearGradient2982"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,0.9999987)"
x1="23.99999"
y1="4.999989"
x2="23.99999"
y2="43" />
<linearGradient
id="linearGradient3308-4-6-931-761">
<stop
id="stop2919"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop2921"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3575"
id="radialGradient2985"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,1.0262008,-1.6561124,9.4072203e-4,-56.097482,-45.332325)"
cx="48.42384"
cy="-48.027504"
fx="48.42384"
fy="-48.027504"
r="38.212933" />
<linearGradient
id="linearGradient3575">
<stop
id="stop3577"
style="stop-color:#fafafa;stop-opacity:1"
offset="0" />
<stop
id="stop3579"
style="stop-color:#e6e6e6;stop-opacity:1"
offset="1" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3993"
id="radialGradient2990"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0,2.0478765,-2.7410544,-8.6412258e-8,47.161382,-8.837436)"
cx="9.3330879"
cy="8.4497671"
fx="9.3330879"
fy="8.4497671"
r="19.99999" />
<linearGradient
id="linearGradient3993">
<stop
offset="0"
style="stop-color:#a3c0d0;stop-opacity:1"
id="stop3995" />
<stop
offset="1"
style="stop-color:#427da1;stop-opacity:1"
id="stop4001" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2508"
id="linearGradient2992"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,0.9674382)"
x1="14.048676"
y1="44.137306"
x2="14.048676"
y2="4.0000005" />
<linearGradient
id="linearGradient2508">
<stop
offset="0"
style="stop-color:#2e4a5a;stop-opacity:1"
id="stop2510" />
<stop
offset="1"
style="stop-color:#6e8796;stop-opacity:1"
id="stop2512" />
</linearGradient>
<radialGradient
cx="4.9929786"
cy="43.5"
r="2.5"
fx="4.9929786"
fy="43.5"
id="radialGradient2873-966-168"
xlink:href="#linearGradient3688-166-749"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)" />
<linearGradient
id="linearGradient3688-166-749">
<stop
id="stop2883"
style="stop-color:#181818;stop-opacity:1"
offset="0" />
<stop
id="stop2885"
style="stop-color:#181818;stop-opacity:0"
offset="1" />
</linearGradient>
<radialGradient
cx="4.9929786"
cy="43.5"
r="2.5"
fx="4.9929786"
fy="43.5"
id="radialGradient2875-742-326"
xlink:href="#linearGradient3688-464-309"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)" />
<linearGradient
id="linearGradient3688-464-309">
<stop
id="stop2889"
style="stop-color:#181818;stop-opacity:1"
offset="0" />
<stop
id="stop2891"
style="stop-color:#181818;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
x1="25.058096"
y1="47.027729"
x2="25.058096"
y2="39.999443"
id="linearGradient2877-634-617"
xlink:href="#linearGradient3702-501-757"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3702-501-757">
<stop
id="stop2895"
style="stop-color:#181818;stop-opacity:0"
offset="0" />
<stop
id="stop2897"
style="stop-color:#181818;stop-opacity:1"
offset="0.5" />
<stop
id="stop2899"
style="stop-color:#181818;stop-opacity:0"
offset="1" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7"
inkscape:cx="24"
inkscape:cy="24"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="603"
inkscape:window-height="484"
inkscape:window-x="417"
inkscape:window-y="162"
inkscape:window-maximized="0" />
<metadata
id="metadata3837">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
style="display:inline"
id="g2036"
transform="matrix(1.1,0,0,0.4444449,-2.4000022,25.11107)">
<g
style="opacity:0.4"
id="g3712"
transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
<rect
style="fill:url(#radialGradient2873-966-168);fill-opacity:1;stroke:none"
id="rect2801"
y="40"
x="38"
height="7"
width="5" />
<rect
style="fill:url(#radialGradient2875-742-326);fill-opacity:1;stroke:none"
id="rect3696"
transform="scale(-1,-1)"
y="-47"
x="-10"
height="7"
width="5" />
<rect
style="fill:url(#linearGradient2877-634-617);fill-opacity:1;stroke:none"
id="rect3700"
y="40"
x="10"
height="7.0000005"
width="28" />
</g>
</g>
<rect
style="fill:url(#radialGradient2990);fill-opacity:1;stroke:url(#linearGradient2992);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect5505"
y="5.4674392"
x="4.5"
ry="2.2322156"
rx="2.2322156"
height="39"
width="39" />
<path
style="opacity:0.05;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00178742;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path4294-1"
d="m 21,6.9687498 a 2.0165107,2.0165107 0 0 0 -2.03125,2.03125 l 0,3.9687502 -1.15625,0 a 2.0165107,2.0165107 0 0 0 -1.5,3.375 l 5.0625,5.75 c -0.06312,0.110777 -0.178724,0.246032 -0.21875,0.34375 -0.195898,0.478256 -0.25,0.83653 -0.25,1.21875 l 0,0.125 L 20.8125,23.6875 C 20.534322,23.409323 20.213169,23.162739 19.71875,22.96875 19.47154,22.87176 19.185456,22.791748 18.75,22.8125 c -0.435456,0.02075 -1.054055,0.210302 -1.46875,0.625 L 15.75,24.96875 c -0.414689,0.414689 -0.604245,1.033294 -0.625,1.46875 -0.02075,0.435456 0.05925,0.721537 0.15625,0.96875 C 15.475241,27.900677 15.721817,28.221821 16,28.5 l 0.09375,0.09375 -0.125,0 c -0.382218,0 -0.740493,0.0541 -1.21875,0.25 -0.239128,0.09795 -0.538285,0.214988 -0.84375,0.53125 -0.305465,0.316262 -0.625,0.914788 -0.625,1.53125 l 0,2.1875 c 0,0.616465 0.319536,1.214989 0.625,1.53125 0.305464,0.316261 0.604622,0.433301 0.84375,0.53125 0.478256,0.195898 0.83653,0.25 1.21875,0.25 l 0.125,0 L 16,35.5 c -0.278175,0.278176 -0.52476,0.599329 -0.71875,1.09375 -0.09699,0.24721 -0.177003,0.533292 -0.15625,0.96875 0.02075,0.435458 0.210304,1.054058 0.625,1.46875 l 1.53125,1.53125 c 0.414691,0.414697 1.033292,0.604245 1.46875,0.625 0.435458,0.02076 0.721537,-0.05926 0.96875,-0.15625 0.494425,-0.19399 0.81557,-0.440568 1.09375,-0.71875 l 0.09375,-0.09375 0,0.125 c 0,0.38222 0.0541,0.740495 0.25,1.21875 0.09795,0.239127 0.214989,0.538285 0.53125,0.84375 0.316261,0.305465 0.914783,0.625 1.53125,0.625 l 2.1875,0 c 0.616466,0 1.214989,-0.319534 1.53125,-0.625 0.316261,-0.305466 0.433302,-0.604622 0.53125,-0.84375 0.195896,-0.478255 0.25,-0.836532 0.25,-1.21875 l 0,-0.125 0.09375,0.09375 c 0.278176,0.278175 0.599329,0.52476 1.09375,0.71875 0.24721,0.09699 0.533292,0.177003 0.96875,0.15625 0.435458,-0.02075 1.054058,-0.210304 1.46875,-0.625 L 32.875,39.03125 C 33.289697,38.616559 33.479245,37.997958 33.5,37.5625 33.52076,37.127042 33.44074,36.840963 33.34375,36.59375 33.14976,36.099325 32.903182,35.77818 32.625,35.5 l -0.09375,-0.09375 0.125,0 c 0.38222,0 0.740494,-0.0541 1.21875,-0.25 0.239128,-0.09795 0.538286,-0.214988 0.84375,-0.53125 0.305464,-0.316262 0.625,-0.914787 0.625,-1.53125 l 0,-2.1875 c 0,-0.61646 -0.319535,-1.214987 -0.625,-1.53125 -0.305465,-0.316263 -0.604621,-0.433301 -0.84375,-0.53125 -0.478257,-0.195898 -0.836532,-0.25 -1.21875,-0.25 l -0.125,0 L 32.625,28.5 c 0.278177,-0.278177 0.52476,-0.599329 0.71875,-1.09375 C 33.44074,27.15904 33.520753,26.872957 33.5,26.4375 33.47925,26.002043 33.289697,25.383443 32.875,24.96875 L 31.34375,23.4375 c -0.414688,-0.414694 -1.03329,-0.604245 -1.46875,-0.625 -0.43546,-0.02076 -0.721537,0.05925 -0.96875,0.15625 -0.494426,0.193991 -0.815572,0.44057 -1.09375,0.71875 l -0.09375,0.09375 0,-0.125 c 0,-0.382218 -0.0541,-0.740493 -0.25,-1.21875 -0.09112,-0.22245 -0.228127,-0.500183 -0.5,-0.78125 l 4.71875,-5.3125 a 2.0165107,2.0165107 0 0 0 -1.5,-3.375 l -1.15625,0 0,-3.9687502 A 2.0165107,2.0165107 0 0 0 27,6.9687498 l -6,0 z M 24.3125,31.25 c 0.427097,0 0.75,0.322904 0.75,0.75 0,0.427096 -0.322903,0.75 -0.75,0.75 -0.427094,0 -0.75,-0.322906 -0.75,-0.75 0,-0.427094 0.322906,-0.75 0.75,-0.75 z" />
<path
style="opacity:0.05;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00178742;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path4294"
d="m 20.90625,8.0312498 a 0.96385067,0.96385067 0 0 0 -0.875,0.96875 l 0,5.0312502 -2.21875,0 A 0.96385067,0.96385067 0 0 0 17.09375,15.625 l 5.78125,6.53125 c -0.158814,0.0616 -0.341836,0.0951 -0.4375,0.1875 -0.169161,0.163386 -0.252971,0.323419 -0.3125,0.46875 -0.119058,0.290663 -0.15625,0.566746 -0.15625,0.84375 l 0,1.65625 C 21.718163,25.40233 21.485871,25.509772 21.25,25.625 l -1.1875,-1.1875 c -0.199651,-0.19965 -0.421433,-0.352095 -0.71875,-0.46875 -0.148659,-0.05833 -0.329673,-0.104846 -0.5625,-0.09375 -0.232827,0.0111 -0.53583,0.09833 -0.75,0.3125 L 16.5,25.71875 c -0.214168,0.214168 -0.301403,0.517173 -0.3125,0.75 -0.0111,0.232827 0.03542,0.41384 0.09375,0.5625 0.116655,0.297321 0.269096,0.519099 0.46875,0.71875 l 1.1875,1.1875 c -0.115228,0.235871 -0.222668,0.468163 -0.3125,0.71875 l -1.65625,0 c -0.277003,0 -0.553087,0.03719 -0.84375,0.15625 -0.145332,0.05953 -0.305363,0.143338 -0.46875,0.3125 -0.163387,0.169162 -0.3125,0.46403 -0.3125,0.78125 l 0,2.1875 c 0,0.317221 0.149114,0.612089 0.3125,0.78125 0.163386,0.169161 0.323419,0.252971 0.46875,0.3125 0.290663,0.119058 0.566746,0.15625 0.84375,0.15625 l 1.65625,0 c 0.08983,0.250587 0.197272,0.482879 0.3125,0.71875 L 16.75,36.25 c -0.199649,0.19965 -0.352095,0.421432 -0.46875,0.71875 -0.05833,0.148659 -0.104846,0.329672 -0.09375,0.5625 0.0111,0.232828 0.09833,0.535831 0.3125,0.75 l 1.53125,1.53125 c 0.214168,0.214172 0.517172,0.301403 0.75,0.3125 0.232828,0.0111 0.41384,-0.03542 0.5625,-0.09375 0.29732,-0.116655 0.519098,-0.269096 0.71875,-0.46875 L 21.25,38.375 c 0.235871,0.115228 0.468164,0.222668 0.71875,0.3125 l 0,1.65625 c 0,0.277003 0.03719,0.553087 0.15625,0.84375 0.05953,0.145331 0.143339,0.305364 0.3125,0.46875 0.169161,0.163386 0.464028,0.3125 0.78125,0.3125 l 2.1875,0 c 0.317221,0 0.612089,-0.149113 0.78125,-0.3125 0.169161,-0.163387 0.252971,-0.323419 0.3125,-0.46875 0.119057,-0.290663 0.15625,-0.566748 0.15625,-0.84375 l 0,-1.65625 c 0.250586,-0.08983 0.482879,-0.197272 0.71875,-0.3125 l 1.1875,1.1875 c 0.19965,0.199649 0.421432,0.352095 0.71875,0.46875 0.148659,0.05833 0.329672,0.104846 0.5625,0.09375 0.232828,-0.0111 0.535831,-0.09833 0.75,-0.3125 L 32.125,38.28125 c 0.214172,-0.214168 0.301403,-0.517172 0.3125,-0.75 0.0111,-0.232828 -0.03542,-0.41384 -0.09375,-0.5625 C 32.227095,36.67143 32.074654,36.449652 31.875,36.25 L 30.6875,35.0625 C 30.802728,34.82663 30.910168,34.594337 31,34.34375 l 1.65625,0 c 0.277004,0 0.553087,-0.03719 0.84375,-0.15625 0.145332,-0.05953 0.305364,-0.143339 0.46875,-0.3125 0.163386,-0.169161 0.3125,-0.46403 0.3125,-0.78125 l 0,-2.1875 c 0,-0.317219 -0.149114,-0.612088 -0.3125,-0.78125 C 33.805364,29.955838 33.645332,29.872029 33.5,29.8125 33.209336,29.693442 32.933253,29.65625 32.65625,29.65625 l -1.65625,0 C 30.91017,29.405663 30.802728,29.17337 30.6875,28.9375 L 31.875,27.75 c 0.19965,-0.19965 0.352095,-0.421432 0.46875,-0.71875 0.05833,-0.148659 0.104846,-0.329672 0.09375,-0.5625 -0.0111,-0.232828 -0.09833,-0.535831 -0.3125,-0.75 L 30.59375,24.1875 c -0.214167,-0.21417 -0.517171,-0.301403 -0.75,-0.3125 -0.232829,-0.0111 -0.41384,0.03542 -0.5625,0.09375 -0.29732,0.116656 -0.519099,0.269097 -0.71875,0.46875 L 27.375,25.625 c -0.235871,-0.115228 -0.468163,-0.222668 -0.71875,-0.3125 l 0,-1.65625 c 0,-0.277003 -0.03719,-0.553087 -0.15625,-0.84375 -0.05953,-0.145332 -0.143338,-0.305363 -0.3125,-0.46875 -0.169162,-0.163387 -0.46403,-0.3125 -0.78125,-0.3125 l -0.15625,0 5.65625,-6.40625 A 0.96385067,0.96385067 0 0 0 30.1875,14.03125 l -2.21875,0 0,-5.0312502 A 0.96385067,0.96385067 0 0 0 27,8.0312498 l -6,0 a 0.96385067,0.96385067 0 0 0 -0.09375,0 z M 24.3125,30.1875 c 1.002113,0 1.8125,0.810388 1.8125,1.8125 0,1.002112 -0.810387,1.8125 -1.8125,1.8125 C 23.31039,33.8125 22.5,33.002111 22.5,32 c 0,-1.002111 0.81039,-1.8125 1.8125,-1.8125 z" />
<path
style="fill:url(#radialGradient2985);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00178742;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path2317"
d="M 21,8.9999996 21,15 17.8125,15 24,22 30.1875,15 27,15 l 0,-6.0000004 -6,0 z M 23.21875,23 c -0.172892,0 -0.28125,0.294922 -0.28125,0.65625 l 0,2.28125 C 22.24145,26.095996 21.585954,26.379869 21,26.75 l -1.625,-1.625 c -0.255498,-0.255497 -0.533998,-0.372253 -0.65625,-0.25 l -1.53125,1.53125 c -0.122254,0.122254 -0.0055,0.400753 0.25,0.65625 l 1.625,1.625 c -0.37013,0.585953 -0.654003,1.24145 -0.8125,1.9375 l -2.28125,0 c -0.361328,0 -0.65625,0.108357 -0.65625,0.28125 l 0,2.1875 c 0,0.172892 0.294922,0.28125 0.65625,0.28125 l 2.28125,0 c 0.158497,0.69605 0.44237,1.351546 0.8125,1.9375 l -1.625,1.625 c -0.255497,0.255498 -0.372254,0.533997 -0.25,0.65625 l 1.53125,1.53125 c 0.122252,0.122254 0.400752,0.0055 0.65625,-0.25 L 21,37.25 c 0.585954,0.37013 1.24145,0.654002 1.9375,0.8125 l 0,2.28125 C 22.9375,40.705077 23.045858,41 23.21875,41 l 2.1875,0 c 0.172893,0 0.28125,-0.294924 0.28125,-0.65625 l 0,-2.28125 c 0.69605,-0.158498 1.351546,-0.44237 1.9375,-0.8125 l 1.625,1.625 c 0.255498,0.255497 0.533997,0.372254 0.65625,0.25 l 1.53125,-1.53125 c 0.122254,-0.122252 0.0055,-0.400752 -0.25,-0.65625 l -1.625,-1.625 c 0.370129,-0.585954 0.654003,-1.24145 0.8125,-1.9375 l 2.28125,0 c 0.361329,0 0.65625,-0.108358 0.65625,-0.28125 l 0,-2.1875 c 0,-0.172893 -0.294921,-0.28125 -0.65625,-0.28125 l -2.28125,0 c -0.158497,-0.69605 -0.442371,-1.351547 -0.8125,-1.9375 l 1.625,-1.625 c 0.255497,-0.255497 0.372254,-0.533997 0.25,-0.65625 L 29.90625,24.875 C 29.783997,24.752745 29.505498,24.8695 29.25,25.125 l -1.625,1.625 c -0.585954,-0.370131 -1.24145,-0.654004 -1.9375,-0.8125 l 0,-2.28125 C 25.6875,23.294922 25.579143,23 25.40625,23 l -2.1875,0 z m 1.09375,6.21875 c 1.528616,0 2.78125,1.252635 2.78125,2.78125 0,1.528615 -1.252634,2.78125 -2.78125,2.78125 -1.528614,0 -2.78125,-1.252635 -2.78125,-2.78125 0,-1.528615 1.252636,-2.78125 2.78125,-2.78125 z" />
<rect
style="opacity:0.4;fill:none;stroke:url(#linearGradient2982);stroke-width:0.99999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect6741"
y="6.4999886"
x="5.4999981"
ry="1.365193"
rx="1.365193"
height="37.000011"
width="36.999985" />
<path
style="fill:none;stroke:url(#linearGradient2979);stroke-width:0.99829447;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
id="path2777"
d="M 28.926376,15.466668 24,21.177578 18.963089,15.5 21.5,15.5 l 0,-6.0000004 5,0 0,6.0000004 2.426376,-0.03333 z" />
<path
style="fill:none;stroke:url(#linearGradient2975);stroke-width:1;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="path4243"
d="m 23.4375,23.46875 c -0.01166,0.05381 -0.03125,0.100205 -0.03125,0.1875 l 0,2.28125 a 0.48185467,0.48185467 0 0 1 -0.375,0.46875 c -0.638467,0.145384 -1.238423,0.407111 -1.78125,0.75 a 0.48185467,0.48185467 0 0 1 -0.59375,-0.0625 l -1.625,-1.625 C 18.9779,25.4154 18.9477,25.40242 18.90625,25.375 l -1.21875,1.21875 c 0.02742,0.04145 0.0404,0.07165 0.09375,0.125 l 1.625,1.625 a 0.48185467,0.48185467 0 0 1 0.0625,0.59375 c -0.342888,0.542826 -0.604615,1.142782 -0.75,1.78125 a 0.48185467,0.48185467 0 0 1 -0.46875,0.375 l -2.28125,0 c -0.08729,0 -0.133695,0.01959 -0.1875,0.03125 l 0,1.75 c 0.05381,0.01166 0.100205,0.03125 0.1875,0.03125 l 2.28125,0 a 0.48185467,0.48185467 0 0 1 0.46875,0.375 c 0.145385,0.638468 0.407112,1.238423 0.75,1.78125 a 0.48185467,0.48185467 0 0 1 -0.0625,0.59375 l -1.625,1.625 c -0.05335,0.05335 -0.06633,0.08355 -0.09375,0.125 l 1.21875,1.21875 c 0.04145,-0.02742 0.07165,-0.0404 0.125,-0.09375 l 1.625,-1.625 A 0.48185467,0.48185467 0 0 1 21.25,36.84375 c 0.542827,0.342888 1.142781,0.604614 1.78125,0.75 a 0.48185467,0.48185467 0 0 1 0.375,0.46875 l 0,2.28125 c 0,0.08729 0.01959,0.133695 0.03125,0.1875 l 1.75,0 c 0.01166,-0.0538 0.03125,-0.100206 0.03125,-0.1875 l 0,-2.28125 a 0.48185467,0.48185467 0 0 1 0.375,-0.46875 c 0.638469,-0.145386 1.238423,-0.407112 1.78125,-0.75 a 0.48185467,0.48185467 0 0 1 0.59375,0.0625 l 1.625,1.625 c 0.05335,0.05335 0.08355,0.06633 0.125,0.09375 l 1.21875,-1.21875 c -0.02742,-0.04145 -0.0404,-0.07165 -0.09375,-0.125 l -1.625,-1.625 a 0.48185467,0.48185467 0 0 1 -0.0625,-0.59375 c 0.342888,-0.542828 0.604615,-1.142783 0.75,-1.78125 a 0.48185467,0.48185467 0 0 1 0.46875,-0.375 l 2.28125,0 c 0.08729,0 0.133695,-0.01959 0.1875,-0.03125 l 0,-1.75 c -0.0538,-0.01166 -0.100204,-0.03125 -0.1875,-0.03125 l -2.28125,0 a 0.48185467,0.48185467 0 0 1 -0.46875,-0.375 c -0.145385,-0.638467 -0.407113,-1.238424 -0.75,-1.78125 a 0.48185467,0.48185467 0 0 1 0.0625,-0.59375 l 1.625,-1.625 c 0.05335,-0.05335 0.06633,-0.08355 0.09375,-0.125 L 29.71875,25.375 c -0.04145,0.02742 -0.07165,0.0404 -0.125,0.09375 l -1.625,1.625 a 0.48185467,0.48185467 0 0 1 -0.59375,0.0625 c -0.542827,-0.342889 -1.142783,-0.604616 -1.78125,-0.75 a 0.48185467,0.48185467 0 0 1 -0.375,-0.46875 l 0,-2.28125 c 0,-0.0873 -0.01959,-0.133695 -0.03125,-0.1875 l -1.75,0 z m 0.875,5.28125 c 1.791829,0 3.25,1.458172 3.25,3.25 0,1.791828 -1.458171,3.25 -3.25,3.25 -1.791827,0 -3.25,-1.458172 -3.25,-3.25 0,-1.791828 1.458173,-3.25 3.25,-3.25 z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

216
buildtools/love-js/love-js.sh Executable file
View file

@ -0,0 +1,216 @@
#!/bin/bash
help="love-js.sh: \n
A tool for assembling love.js projects without relying on npm / node. \n\
For the full project please see https://github.com/Davidobot/love.js \n\
\n\
usage: \n\
./love-js.sh [love-file] [project-name] [opts]\n\
\n
opts:\n
-v= (--version=) Version of the Game. Will be included in file name.\n
-o= (--output-directory=) Target directory. Defaults to PWD\n
-t= (--text-colour=) Text Colour. Defaults to \"240,234,214\"\n
-c= (--canvas-colour=) Canvas Colour. Defaults to \"54,69,79\"\n
-a= (--author=) Author (Shows up on loading page).\n
-w= (--width=) Canvas Width (game width in conf). Defaults to 800\n
-h= (--height=) Canvas Height (game height in conf). Defaults to 600\n
-r (--run) Set flag if you want to run a local copy on port 8000\n
-d (--debug) Set flag for debug info\n
-h (--help) Display this text\n
\n
eg: \n\
Pack up sample.love\n
\t./love-js.sh sample.love sample\n
\t> sample-web.zip\n
\n
Pack up sample.love version 0.1.0\n
\t./love-js.sh sample-0.1.0.love sample -v=0.1.0\n
\t> sample-0.1.0-web.zip\n
\n
Pack up sample.love and set the background colour to black\n
\t./love-js.sh sample-0.1.0.love sample -v=0.1.0 -c=\"0,0,0\"\n
\t> sample-0.1.0-web.zip\n
\n
Pack up sample.love and set the author to \"Sample Name\"\n
\t./love-js.sh sample-0.1.0.love sample -a=\"Sample Name\"\n
\t> sample-web.zip"
if [ "$#" -lt "2" ]
then
echo "ERROR! love-js.sh expects at least two arguments."
echo -e $help
exit 1
fi
# use gdu on macOS, fixes 'invalid option -b' error
if [ "$(uname -s)" = "Darwin" ]; then
DU_CMD=gdu
else
DU_CMD=du
fi
love_file=$1
name=$2
## confirm that $release_dir/$name-$version.love exists
if [ ! -f $love_file ]; then
echo "love file not found!"
echo $love_file
exit 1
fi
if [ -f /proc/sys/kernel/random/uuid ]; then
uuid=$(cat /proc/sys/kernel/random/uuid)
else
uuid="2fd99e56-5455-45dd-86dd-7af724874d65"
fi
author="";
run=false;
debug=false;
gethelp=false;
width=800
height=600
release="compat"
love_version="11.5"
canvas_colour="54,69,79"
text_colour="240,234,214"
initial_memory=0
module_size=$($DU_CMD -b $love_file | awk '{print $1}')
title=$(echo $name | sed -r 's/\<./\U&/g' | sed -r 's/-/\ /g')
version=""
dash_version=""
output_dir=$(pwd)
cachemodule="true"
for i in "$@"
do
case $i in
-v=*|--version=*)
version="${i#*=}"
dash_version="-${i#*=}"
;;
-o=*|--output-directory=*)
output_dir="${i#*=}"
;;
-w=*|--width=*)
width="${i#*=}"
;;
-h=*|--height=*)
height="${i#*=}"
;;
-t=*|--text-colour=*)
text_colour="${i#*=}"
;;
-c=*|--canvas-colour=*)
canvas_colour="${i#*=}"
;;
-a=*|--author=*)
author="${i#*=}"
;;
-r|--run)
run=true
;;
-d|--debug)
debug=true
;;
-h|--help)
gethelp=true
;;
-n|--no-cache)
cachemodule="false"
;;
*)
# unknown option
;;
esac
done
page_colour=$canvas_colour
file_name=$output_dir/$name$dash_version-web
debug (){
if [ $debug = true ]
then
echo ""
echo "Debug: love-js.sh"
echo "love file: ${love_file}"
echo "output file: $file_name"
echo "author: $author"
echo "version: $version"
echo "text colour: $text_colour"
echo "canvas colour: $canvas_colour"
echo "canvas size: ${height}, ${width}"
echo "run: ${run}"
echo "use cache: $cachemodule"
fi
}
call_dir=$(pwd)
root="$(dirname "$0")"
build(){
rm -fr $file_name
mkdir -p $file_name && mkdir -p $file_name/theme
cat $root/src/index.html | \
sed "s/{{title}}/${title}/g" | \
sed "s/{{version}}/${version}/g" | \
sed "s/{{author}}/${author}/g" | \
sed "s/{{width}}/${width}/g" | \
sed "s/{{height}}/${height}/g" | \
sed "s/{{initial-memory}}/${initial_memory}/g" | \
sed "s/{{canvas-colour}}/${canvas_colour}/g" | \
sed "s/{{text-colour}}/${text_colour}/g" > \
$file_name/index.html
cat $root/src/love.css | \
sed "s/{{page-colour}}/${page_colour}/g" > \
$file_name/theme/love.css
cat $root/src/game.js | \
sed "s/{{{cachemodule}}}/${cachemodule}/g" | \
sed "s/{{{metadata}}}/{\"package_uuid\":\"${uuid}\",\"remote_package_size\":$module_size,\"files\":[{\"filename\":\"\/game.love\",\"crunched\":0,\"start\":0,\"end\":$module_size,\"audio\":false}]}/" > \
$file_name/game.js
cp $root/src/serve.py $file_name
cp $root/src/consolewrapper.js $file_name
cp $love_file $file_name/game.love
cp $root/src/love-$love_version/$release/love.js $file_name
cp $root/src/love-$love_version/$release/love.wasm $file_name
if [ $release == "release" ]; then
cp $root/src/release/love.worker.js $file_name
fi
}
clean(){
rm -rf $file_name
}
release (){
rm -fr $file_name
build
debug
zip -r -q tmp $file_name
rm -f $file_name.zip
mv tmp.zip $file_name.zip
clean
}
run (){
debug
build
cd $file_name
python3 serve.py
}
if [ $gethelp = true ]
then
echo -e $help
elif [ $run = false ]
then
release
else
run
fi

View file

@ -0,0 +1,99 @@
var newConsole = (function(oldConsole)
{
return {
log : function()
{
var data = [];
for (var _i = 0; _i < arguments.length; _i++) {
data[_i] = arguments[_i];
}
if(data.length == 1) //Start looking for api's (And dont show anything)
{
if(typeof(data[0]) == "string" && data[0].indexOf("callJavascriptFunction") != -1) //Contains function
{
// oldConsole.log(data[0]);
try
{
return eval(data[0].split("callJavascriptFunction ")[1]);
}
catch(e)
{
oldConsole.error("Something went wrong with your callJS: \nCode: " + data[0].split("callJavascriptFunction ")[1] + "\nError: '" + e.message + "'");
return null;
}
}
else
{
oldConsole.log(data[0]);
return null;
}
}
else
oldConsole.log(data[0], data.splice(1));
return null;
},
warn : function()
{
var data = [];
for (var _i = 0; _i < arguments.length; _i++) {
data[_i] = arguments[_i];
}
if(data.length == 1)
oldConsole.warn(data[0]);
else
oldConsole.warn(data[0], data.splice(1));
},
error : function()
{
var data = [];
for (var _i = 0; _i < arguments.length; _i++) {
data[_i] = arguments[_i];
}
if(data.length == 1)
oldConsole.error(data[0]);
else
oldConsole.error(data[0], data.splice(1));
},
info : function()
{
var data = [];
for (var _i = 0; _i < arguments.length; _i++) {
data[_i] = arguments[_i];
}
if(data.length == 1)
oldConsole.info(data[0]);
else
oldConsole.info(data[0], data.splice(1));
},
clear : function()
{
oldConsole.clear()
},
assert : function()
{
for (var _i = 0; _i < arguments.length; _i++) {
data[_i] = arguments[_i];
}
oldConsole.assert(data[0], data[1], data.splice(2));
},
group : function()
{
for (var _i = 0; _i < arguments.length; _i++) {
data[_i] = arguments[_i];
}
oldConsole.group(data[0], data.splice(1));
},
groupCollapsed : function()
{
for (var _i = 0; _i < arguments.length; _i++) {
data[_i] = arguments[_i];
}
oldConsole.groupCollapsed(data[0], data.splice(1));
},
groupEnd : function()
{
oldConsole.groupEnd()
}
}
}(window.console));
window.console = newConsole;

View file

@ -0,0 +1,295 @@
var Module;
var CACHEMODULE = {{{cachemodule}}};
if (typeof Module === 'undefined') Module = eval('(function() { try { return Module || {} } catch(e) { return {} } })()');
if (!Module.expectedDataFileDownloads) {
Module.expectedDataFileDownloads = 0;
Module.finishedDataFileDownloads = 0;
}
Module.expectedDataFileDownloads++;
(function() {
var loadPackage = function(metadata) {
var PACKAGE_PATH;
if (typeof window === 'object') {
PACKAGE_PATH = window['encodeURIComponent'](window.location.pathname.toString().substring(0, window.location.pathname.toString().lastIndexOf('/')) + '/');
} else if (typeof location !== 'undefined') {
// worker
PACKAGE_PATH = encodeURIComponent(location.pathname.toString().substring(0, location.pathname.toString().lastIndexOf('/')) + '/');
} else {
throw 'using preloaded data can only be done on a web page or in a web worker';
}
var PACKAGE_NAME = 'game.love';
var REMOTE_PACKAGE_BASE = 'game.love';
if (typeof Module['locateFilePackage'] === 'function' && !Module['locateFile']) {
Module['locateFile'] = Module['locateFilePackage'];
Module.printErr('warning: you defined Module.locateFilePackage, that has been renamed to Module.locateFile (using your locateFilePackage for now)');
}
var REMOTE_PACKAGE_NAME = typeof Module['locateFile'] === 'function' ?
Module['locateFile'](REMOTE_PACKAGE_BASE) :
((Module['filePackagePrefixURL'] || '') + REMOTE_PACKAGE_BASE);
var REMOTE_PACKAGE_SIZE = metadata.remote_package_size;
var PACKAGE_UUID = metadata.package_uuid;
function fetchRemotePackage(packageName, packageSize, callback, errback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', packageName, true);
xhr.responseType = 'arraybuffer';
xhr.onprogress = function(event) {
var url = packageName;
var size = packageSize;
if (event.total) size = event.total;
if (event.loaded) {
if (!xhr.addedTotal) {
xhr.addedTotal = true;
if (!Module.dataFileDownloads) Module.dataFileDownloads = {};
Module.dataFileDownloads[url] = {
loaded: event.loaded,
total: size
};
} else {
Module.dataFileDownloads[url].loaded = event.loaded;
}
var total = 0;
var loaded = 0;
var num = 0;
for (var download in Module.dataFileDownloads) {
var data = Module.dataFileDownloads[download];
total += data.total;
loaded += data.loaded;
num++;
}
total = Math.ceil(total * Module.expectedDataFileDownloads/num);
if (Module['setStatus']) Module['setStatus']('Downloading data... (' + loaded + '/' + total + ')');
} else if (!Module.dataFileDownloads) {
if (Module['setStatus']) Module['setStatus']('Downloading data...');
}
};
xhr.onerror = function(event) {
throw new Error("NetworkError for: " + packageName);
}
xhr.onload = function(event) {
if (xhr.status == 200 || xhr.status == 304 || xhr.status == 206 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0
var packageData = xhr.response;
callback(packageData);
} else {
throw new Error(xhr.statusText + " : " + xhr.responseURL);
}
};
xhr.send(null);
};
function handleError(error) {
console.error('package error:', error);
};
function runWithFS() {
function assert(check, msg) {
if (!check) throw msg + new Error().stack;
}
// {{{create_file_paths}}}
function DataRequest(start, end, crunched, audio) {
this.start = start;
this.end = end;
this.crunched = crunched;
this.audio = audio;
}
DataRequest.prototype = {
requests: {},
open: function(mode, name) {
this.name = name;
this.requests[name] = this;
Module['addRunDependency']('fp ' + this.name);
},
send: function() {},
onload: function() {
var byteArray = this.byteArray.subarray(this.start, this.end);
this.finish(byteArray);
},
finish: function(byteArray) {
var that = this;
Module['FS_createDataFile'](this.name, null, byteArray, true, true, true); // canOwn this data in the filesystem, it is a slide into the heap that will never change
Module['removeRunDependency']('fp ' + that.name);
this.requests[this.name] = null;
}
};
var files = metadata.files;
for (i = 0; i < files.length; ++i) {
new DataRequest(files[i].start, files[i].end, files[i].crunched, files[i].audio).open('GET', files[i].filename);
}
var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
var IDB_RO = "readonly";
var IDB_RW = "readwrite";
var DB_NAME = "EM_PRELOAD_CACHE";
var DB_VERSION = 1;
var METADATA_STORE_NAME = 'METADATA';
var PACKAGE_STORE_NAME = 'PACKAGES';
function openDatabase(callback, errback) {
try {
var openRequest = indexedDB.open(DB_NAME, DB_VERSION);
} catch (e) {
return errback(e);
}
openRequest.onupgradeneeded = function(event) {
var db = event.target.result;
if(db.objectStoreNames.contains(PACKAGE_STORE_NAME)) {
db.deleteObjectStore(PACKAGE_STORE_NAME);
}
var packages = db.createObjectStore(PACKAGE_STORE_NAME);
if(db.objectStoreNames.contains(METADATA_STORE_NAME)) {
db.deleteObjectStore(METADATA_STORE_NAME);
}
var metadata = db.createObjectStore(METADATA_STORE_NAME);
};
openRequest.onsuccess = function(event) {
var db = event.target.result;
callback(db);
};
openRequest.onerror = function(error) {
errback(error);
};
};
/* Check if there's a cached package, and if so whether it's the latest available */
function checkCachedPackage(db, packageName, callback, errback) {
var transaction = db.transaction([METADATA_STORE_NAME], IDB_RO);
var metadata = transaction.objectStore(METADATA_STORE_NAME);
var getRequest = metadata.get("metadata/" + packageName);
getRequest.onsuccess = function(event) {
var result = event.target.result;
if (!result) {
return callback(false);
} else {
return callback(PACKAGE_UUID === result.uuid);
}
};
getRequest.onerror = function(error) {
errback(error);
};
};
function fetchCachedPackage(db, packageName, callback, errback) {
var transaction = db.transaction([PACKAGE_STORE_NAME], IDB_RO);
var packages = transaction.objectStore(PACKAGE_STORE_NAME);
var getRequest = packages.get("package/" + packageName);
getRequest.onsuccess = function(event) {
var result = event.target.result;
callback(result);
};
getRequest.onerror = function(error) {
errback(error);
};
};
function cacheRemotePackage(db, packageName, packageData, packageMeta, callback, errback) {
var transaction_packages = db.transaction([PACKAGE_STORE_NAME], IDB_RW);
var packages = transaction_packages.objectStore(PACKAGE_STORE_NAME);
var putPackageRequest = packages.put(packageData, "package/" + packageName);
putPackageRequest.onsuccess = function(event) {
var transaction_metadata = db.transaction([METADATA_STORE_NAME], IDB_RW);
var metadata = transaction_metadata.objectStore(METADATA_STORE_NAME);
var putMetadataRequest = metadata.put(packageMeta, "metadata/" + packageName);
putMetadataRequest.onsuccess = function(event) {
callback(packageData);
};
putMetadataRequest.onerror = function(error) {
errback(error);
};
};
putPackageRequest.onerror = function(error) {
errback(error);
};
};
function processPackageData(arrayBuffer) {
Module.finishedDataFileDownloads++;
assert(arrayBuffer, 'Loading data file failed.');
assert(arrayBuffer instanceof ArrayBuffer, 'bad input to processPackageData');
var byteArray = new Uint8Array(arrayBuffer);
var curr;
// copy the entire loaded file into a spot in the heap. Files will refer to slices in that. They cannot be freed though
// (we may be allocating before malloc is ready, during startup).
if (Module['SPLIT_MEMORY']) Module.printErr('warning: you should run the file packager with --no-heap-copy when SPLIT_MEMORY is used, otherwise copying into the heap may fail due to the splitting');
var ptr = Module['getMemory'](byteArray.length);
Module['HEAPU8'].set(byteArray, ptr);
DataRequest.prototype.byteArray = Module['HEAPU8'].subarray(ptr, ptr+byteArray.length);
var files = metadata.files;
for (i = 0; i < files.length; ++i) {
DataRequest.prototype.requests[files[i].filename].onload();
}
Module['removeRunDependency']('datafile_game.data');
};
Module['addRunDependency']('datafile_game.data');
if (!Module.preloadResults) Module.preloadResults = {};
function preloadFallback(error) {
console.error(error);
console.error('falling back to default preload behavior');
fetchRemotePackage(REMOTE_PACKAGE_NAME, REMOTE_PACKAGE_SIZE, processPackageData, handleError);
};
openDatabase(
function(db) {
checkCachedPackage(db, PACKAGE_PATH + PACKAGE_NAME,
function(useCached) {
Module.preloadResults[PACKAGE_NAME] = {fromCache: useCached};
if (useCached && CACHEMODULE) {
console.info('loading ' + PACKAGE_NAME + ' from cache');
fetchCachedPackage(db, PACKAGE_PATH + PACKAGE_NAME, processPackageData, preloadFallback);
} else {
console.info('loading ' + PACKAGE_NAME + ' from remote');
fetchRemotePackage(REMOTE_PACKAGE_NAME, REMOTE_PACKAGE_SIZE,
function(packageData) {
if(CACHEMODULE){
cacheRemotePackage(db, PACKAGE_PATH + PACKAGE_NAME, packageData, {uuid:PACKAGE_UUID}, processPackageData,
function(error) {
console.error(error);
processPackageData(packageData);
});}
else {
processPackageData(packageData);
}
}
, preloadFallback);
}
}
, preloadFallback);
}
, preloadFallback);
if (Module['setStatus']) Module['setStatus']('Downloading...');
}
if (Module['calledRun']) {
runWithFS();
} else {
if (!Module['preRun']) Module['preRun'] = [];
Module["preRun"].push(runWithFS); // FS is not initialized yet, wait for it
}
}
loadPackage({{{metadata}}});
})();

View file

@ -0,0 +1,162 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>{{title}}</title>
<script src = "consolewrapper.js"></script>
<!-- Load custom style sheet -->
<link rel="stylesheet" type="text/css" href="theme/love.css">
</head>
<body>
<center>
<div>
<canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<canvas id="loadingCanvas" oncontextmenu="event.preventDefault()"
width="{{width}}" height="{{height}}"></canvas>
</div>
</center>
<script type='text/javascript'>
function goFullScreen(){
var canvas = document.getElementById("canvas");
if(canvas.requestFullScreen)
canvas.requestFullScreen();
else if(canvas.webkitRequestFullScreen)
canvas.webkitRequestFullScreen();
else if(canvas.mozRequestFullScreen)
canvas.mozRequestFullScreen();
}
function closeFullScreen() {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) { /* Safari */
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) { /* IE11 */
document.msExitFullscreen();
}
}
function toggleFullScreen(){
if((window.fullScreen) || /* firefox */
(window.innerWidth == screen.width && /* everything else */
window.innerHeight == screen.height)) {
closeFullScreen();
} else {
goFullScreen();
}
}
var loadingContext = document.getElementById('loadingCanvas').getContext('2d');
var complete = false;
function drawLoadingText(text, soFar, total) {
var canvas = loadingContext.canvas;
var ratio = complete ? 1 : 0;
if (soFar && total){
ratio = soFar / total
}
if (ratio == 1){
complete = true
}
//
loadingContext.fillStyle = "rgb({{canvas-colour}})";
loadingContext.fillRect(0, 0, canvas.scrollWidth, canvas.scrollHeight);
loadingContext.font = '2em arial';
loadingContext.textAlign = 'center'
//
loadingContext.fillStyle = "rgb({{text-colour}})";
loadingContext.fillText(text, canvas.scrollWidth / 2, (canvas.scrollHeight / 2) - 40);
//
loadingContext.beginPath();
loadingContext.strokeStyle = "rgb({{text-colour}})";
loadingContext.rect((canvas.scrollWidth / 2) - 200,
( canvas.scrollHeight / 2) - 20,
400,
40
);
loadingContext.stroke();
loadingContext.beginPath();
loadingContext.rect((canvas.scrollWidth / 2) - 200,
( canvas.scrollHeight / 2) - 20,
400 * ratio,
40
);
loadingContext.fill();
loadingContext.font = '4em arial';
loadingContext.fillText("{{title}}", canvas.scrollWidth / 2, canvas.scrollHeight / 4);
loadingContext.font = '2em arial';
loadingContext.fillText("{{version}}", canvas.scrollWidth / 2, (canvas.scrollHeight / 4 + 45));
loadingContext.font = '1em arial';
loadingContext.textAlign = 'left'
loadingContext.fillText("Powered By LÖVE", canvas.scrollWidth / 2 - 300, canvas.scrollHeight / 4 * 3);
loadingContext.textAlign = 'right'
loadingContext.fillText("Game By: {{author}}", canvas.scrollWidth / 2 + 300, canvas.scrollHeight / 4 * 3);
}
window.onload = function () { window.focus(); };
window.onclick = function () { window.focus(); };
window.addEventListener("keydown", function(e) {
// space and arrow keys
if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
e.preventDefault();
}
}, false);
var Module = {
arguments: ["game.love","web"],
INITIAL_MEMORY: {{initial-memory}},
printErr: console.error.bind(console),
canvas: (function() {
var canvas = document.getElementById('canvas');
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
// application robust, you may want to override this behavior before shipping!
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
return canvas;
})(),
setStatus: function(text, soFar, total) {
if (text) {
drawLoadingText(text, soFar, total);
} else if (Module.remainingDependencies === 0) {
document.getElementById('loadingCanvas').style.display = 'none';
document.getElementById('canvas').style.display = 'block';
}
},
totalDependencies: 0,
remainingDependencies: 0,
monitorRunDependencies: function(left) {
this.remainingDependencies = left;
this.totalDependencies = Math.max(this.totalDependencies, left);
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.',
this.totalDependencies-left,
this.totalDependencies);
}
};
Module.setStatus('Downloading...');
window.onerror = function(event) {
// TODO: do not warn on ok events like simulating an infinite loop or exitStatus
Module.setStatus('Exception thrown, see JavaScript console');
Module.setStatus = function(text) {
if (text) Module.printErr('[post-exception status] ' + text);
};
};
var applicationLoad = function(e) {
Love(Module);
}
</script>
<script type="text/javascript" src="game.js"></script>
<script async type="text/javascript" src="love.js" onload="applicationLoad(this)"></script>
<!-- <footer> -->
<!-- <button onclick="goFullScreen();">Go Fullscreen</button> -->
<!-- </footer> -->
</body>
</html>

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -0,0 +1 @@
var threadInfoStruct=0;var selfThreadId=0;var parentThreadId=0;var Module={};function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:selfThreadId})}var err=threadPrintErr;this.alert=threadAlert;Module["instantiateWasm"]=function(info,receiveInstance){var instance=new WebAssembly.Instance(Module["wasmModule"],info);Module["wasmModule"]=null;receiveInstance(instance);return instance.exports};this.onmessage=function(e){try{if(e.data.cmd==="load"){Module["DYNAMIC_BASE"]=e.data.DYNAMIC_BASE;Module["DYNAMICTOP_PTR"]=e.data.DYNAMICTOP_PTR;Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;if(typeof e.data.urlOrBlob==="string"){importScripts(e.data.urlOrBlob)}else{var objectUrl=URL.createObjectURL(e.data.urlOrBlob);importScripts(objectUrl);URL.revokeObjectURL(objectUrl)}Love(Module).then(function(instance){Module=instance;postMessage({"cmd":"loaded"})})}else if(e.data.cmd==="objectTransfer"){Module["PThread"].receiveObjectTransfer(e.data)}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;threadInfoStruct=e.data.threadInfoStruct;Module["registerPthreadPtr"](threadInfoStruct,0,0);selfThreadId=e.data.selfThreadId;parentThreadId=e.data.parentThreadId;var max=e.data.stackBase;var top=e.data.stackBase+e.data.stackSize;Module["establishStackSpace"](top,max);Module["_emscripten_tls_init"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].setThreadStatus(Module["_pthread_self"](),1);try{var result=Module["dynCall"]("ii",e.data.start_routine,[e.data.arg]);if(!Module["getNoExitRuntime"]())Module["PThread"].threadExit(result)}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){Atomics.store(Module["HEAPU32"],threadInfoStruct+4>>2,ex instanceof Module["ExitStatus"]?ex.status:-2);Atomics.store(Module["HEAPU32"],threadInfoStruct+0>>2,1);Module["_emscripten_futex_wake"](threadInfoStruct+0,2147483647);if(!(ex instanceof Module["ExitStatus"]))throw ex}}}else if(e.data.cmd==="cancel"){if(threadInfoStruct){Module["PThread"].threadCancel()}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(threadInfoStruct){Module["_emscripten_current_thread_process_queued_calls"]()}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex.stack)err(ex.stack);throw ex}};if(typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string"){self={location:{href:__filename}};var onmessage=this.onmessage;var nodeWorkerThreads=require("worker_threads");global.Worker=nodeWorkerThreads.Worker;var parentPort=nodeWorkerThreads.parentPort;parentPort.on("message",function(data){onmessage({data:data})});var nodeFS=require("fs");var nodeRead=function(filename){return nodeFS.readFileSync(filename,"utf8")};function globalEval(x){global.require=require;global.Module=Module;eval.call(null,x)}importScripts=function(f){globalEval(nodeRead(f))};postMessage=function(msg){parentPort.postMessage(msg)};if(typeof performance==="undefined"){performance={now:function(){return Date.now()}}}}

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -0,0 +1 @@
var threadInfoStruct=0;var selfThreadId=0;var parentThreadId=0;var Module={};function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:selfThreadId})}var err=threadPrintErr;this.alert=threadAlert;Module["instantiateWasm"]=function(info,receiveInstance){var instance=new WebAssembly.Instance(Module["wasmModule"],info);Module["wasmModule"]=null;receiveInstance(instance);return instance.exports};this.onmessage=function(e){try{if(e.data.cmd==="load"){Module["DYNAMIC_BASE"]=e.data.DYNAMIC_BASE;Module["DYNAMICTOP_PTR"]=e.data.DYNAMICTOP_PTR;Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;if(typeof e.data.urlOrBlob==="string"){importScripts(e.data.urlOrBlob)}else{var objectUrl=URL.createObjectURL(e.data.urlOrBlob);importScripts(objectUrl);URL.revokeObjectURL(objectUrl)}Love(Module).then(function(instance){Module=instance;postMessage({"cmd":"loaded"})})}else if(e.data.cmd==="objectTransfer"){Module["PThread"].receiveObjectTransfer(e.data)}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;threadInfoStruct=e.data.threadInfoStruct;Module["registerPthreadPtr"](threadInfoStruct,0,0);selfThreadId=e.data.selfThreadId;parentThreadId=e.data.parentThreadId;var max=e.data.stackBase;var top=e.data.stackBase+e.data.stackSize;Module["establishStackSpace"](top,max);Module["_emscripten_tls_init"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].setThreadStatus(Module["_pthread_self"](),1);try{var result=Module["dynCall_ii"](e.data.start_routine,e.data.arg);if(!Module["getNoExitRuntime"]())Module["PThread"].threadExit(result)}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){Atomics.store(Module["HEAPU32"],threadInfoStruct+4>>2,ex instanceof Module["ExitStatus"]?ex.status:-2);Atomics.store(Module["HEAPU32"],threadInfoStruct+0>>2,1);Module["_emscripten_futex_wake"](threadInfoStruct+0,2147483647);if(!(ex instanceof Module["ExitStatus"]))throw ex}}}else if(e.data.cmd==="cancel"){if(threadInfoStruct){Module["PThread"].threadCancel()}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(threadInfoStruct){Module["_emscripten_current_thread_process_queued_calls"]()}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex.stack)err(ex.stack);throw ex}};if(typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string"){self={location:{href:__filename}};var onmessage=this.onmessage;var nodeWorkerThreads=require("worker_threads");global.Worker=nodeWorkerThreads.Worker;var parentPort=nodeWorkerThreads.parentPort;parentPort.on("message",function(data){onmessage({data:data})});var nodeFS=require("fs");var nodeRead=function(filename){return nodeFS.readFileSync(filename,"utf8")};function globalEval(x){global.require=require;global.Module=Module;eval.call(null,x)}importScripts=function(f){globalEval(nodeRead(f))};postMessage=function(msg){parentPort.postMessage(msg)};if(typeof performance==="undefined"){performance={now:function(){return Date.now()}}}}

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -0,0 +1 @@
"use strict";var Module={};var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";if(ENVIRONMENT_IS_NODE){var nodeWorkerThreads=require("worker_threads");var parentPort=nodeWorkerThreads.parentPort;parentPort.on("message",data=>onmessage({data:data}));var fs=require("fs");var vm=require("vm");Object.assign(global,{self:global,require:require,Module:Module,location:{href:__filename},Worker:nodeWorkerThreads.Worker,importScripts:f=>vm.runInThisContext(fs.readFileSync(f,"utf8"),{filename:f}),postMessage:msg=>parentPort.postMessage(msg),performance:global.performance||{now:Date.now}})}var initializedJS=false;function threadPrintErr(...args){var text=args.join(" ");if(ENVIRONMENT_IS_NODE){fs.writeSync(2,text+"\n");return}console.error(text)}function threadAlert(...args){var text=args.join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=(info,receiveInstance)=>{var module=Module["wasmModule"];Module["wasmModule"]=null;var instance=new WebAssembly.Instance(module,info);return receiveInstance(instance,module)};self.onunhandledrejection=e=>{throw e.reason||e};function handleMessage(e){try{if(e.data.cmd==="load"){let messageQueue=[];self.onmessage=e=>messageQueue.push(e);self.startWorker=instance=>{Module=instance;postMessage({"cmd":"loaded"});for(let msg of messageQueue){handleMessage(msg)}self.onmessage=handleMessage};Module["wasmModule"]=e.data.wasmModule;for(const handler of e.data.handlers){Module[handler]=(...args)=>{postMessage({cmd:"callHandler",handler:handler,args:args})}}Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;if(typeof e.data.urlOrBlob=="string"){importScripts(e.data.urlOrBlob)}else{var objectUrl=URL.createObjectURL(e.data.urlOrBlob);importScripts(objectUrl);URL.revokeObjectURL(objectUrl)}Love(Module)}else if(e.data.cmd==="run"){Module["__emscripten_thread_init"](e.data.pthread_ptr,0,0,1);Module["__emscripten_thread_mailbox_await"](e.data.pthread_ptr);Module["establishStackSpace"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInitTLS();if(!initializedJS){initializedJS=true}try{Module["invokeEntryPoint"](e.data.start_routine,e.data.arg)}catch(ex){if(ex!="unwind"){throw ex}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["__emscripten_thread_exit"](-1)}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="checkMailbox"){if(initializedJS){Module["checkMailbox"]()}}else if(e.data.cmd){err(`worker.js received unknown command ${e.data.cmd}`);err(e.data)}}catch(ex){Module["__emscripten_thread_crashed"]?.();throw ex}}self.onmessage=handleMessage;

View file

@ -0,0 +1,48 @@
* {
box-sizing: border-box;
}
h1 {
font-family: arial;
color: rgb( 11, 86, 117 );
}
body {
background-repeat: no-repeat;
font-family: arial;
margin: 0;
padding: none;
background-color: rgb({{page-colour}});
color: rgb( 28, 78, 104 );
}
footer {
font-family: arial;
font-size: 12px;
padding-left: 10px;
padding-bottom: 10px;
position:absolute;
bottom: 0;
width: 100%;
}
/* Links */
a {
text-decoration: none;
}
a:link {
color: rgb( 233, 73, 154 );
}
a:visited {
color: rgb( 110, 30, 71 );
}
a:hover {
color: rgb( 252, 207, 230 );
}
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
#canvas {
padding-right: 0;
display: none;
border: 0px none;
}

View file

@ -0,0 +1,22 @@
#!/usr/bin/env python
# Attribution: https://stackoverflow.com/questions/21956683/enable-access-control-on-simple-http-server
try:
# Python 3
from http.server import HTTPServer, SimpleHTTPRequestHandler, test as test_orig
import sys
def test (*args):
test_orig(*args, port=int(sys.argv[1]) if len(sys.argv) > 1 else 8000)
except ImportError: # Python 2
from BaseHTTPServer import HTTPServer, test
from SimpleHTTPServer import SimpleHTTPRequestHandler
class CORSRequestHandler (SimpleHTTPRequestHandler):
def end_headers (self):
self.send_header('Cross-Origin-Opener-Policy', 'same-origin')
self.send_header('Cross-Origin-Embedder-Policy', 'require-corp')
SimpleHTTPRequestHandler.end_headers(self)
if __name__ == '__main__':
test(CORSRequestHandler, HTTPServer)

1053
buildtools/love-release.sh Executable file

File diff suppressed because it is too large Load diff

13
conf.lua Normal file
View file

@ -0,0 +1,13 @@
love.conf = function(t)
t.gammacorrect = true
t.title, t.identity = "Survivor", "Bill Niblock"
t.modules.joystick = false
t.modules.physics = false
-- This scales the game, and it still looks quite nice at 1440p!
-- t.window.fullscreen = true
-- t.window.fullscreentype = "desktop"
t.window.width = 800
t.window.height = 600
t.window.vsync = false
t.version = "11.5"
end

41
error-mode.fnl Normal file
View file

@ -0,0 +1,41 @@
;; This mode has two purposes:
;; * display the stack trace that caused the error
;; * allow the user to decide whether to retry after reloading or quit
;; Since we can't know which module needs to be reloaded, we rely on the user
;; doing a ,reload foo in the repl.
(local state {:msg "" :traceback "" :old-mode :intro})
(local explanation "Press escape to quit.
Press space to return to the previous mode after reloading in the repl.")
(fn draw []
(love.graphics.clear 0.34 0.61 0.86)
(love.graphics.setColor 0.9 0.9 0.9)
(love.graphics.print explanation 15 10)
(love.graphics.print state.msg 10 60)
(love.graphics.print state.traceback 15 125))
(fn keypressed [key set-mode]
(match key
:escape (love.event.quit)
:space (set-mode state.old-mode)))
(fn color-msg [msg]
;; convert compiler's ansi escape codes to love2d-friendly codes
(case (msg:match "(.*)\027%[7m(.*)\027%[0m(.*)")
(pre selected post) [[1 1 1] pre
[1 0.2 0.2] selected
[1 1 1] post]
_ msg))
(fn activate [old-mode msg traceback]
(love.graphics.setNewFont 16) ; use a monospace font here if you have one
(print msg)
(print traceback)
(set state.old-mode old-mode)
(set state.msg (color-msg msg))
(set state.traceback traceback))
{: draw : keypressed : activate}

6991
lib/fennel Executable file

File diff suppressed because one or more lines are too long

6468
lib/fennel.lua Normal file

File diff suppressed because one or more lines are too long

780
lib/lume.lua Normal file
View file

@ -0,0 +1,780 @@
--
-- lume
--
-- Copyright (c) 2018 rxi
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
-- this software and associated documentation files (the "Software"), to deal in
-- the Software without restriction, including without limitation the rights to
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do
-- so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.
--
-- 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
--
local lume = { _version = "2.3.0" }
local pairs, ipairs = pairs, ipairs
local type, assert, unpack = type, assert, unpack or table.unpack
local tostring, tonumber = tostring, tonumber
local math_floor = math.floor
local math_ceil = math.ceil
local math_atan2 = math.atan2 or math.atan
local math_sqrt = math.sqrt
local math_abs = math.abs
local noop = function()
end
local identity = function(x)
return x
end
local patternescape = function(str)
return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%1")
end
local absindex = function(len, i)
return i < 0 and (len + i + 1) or i
end
local iscallable = function(x)
if type(x) == "function" then return true end
local mt = getmetatable(x)
return mt and mt.__call ~= nil
end
local getiter = function(x)
if lume.isarray(x) then
return ipairs
elseif type(x) == "table" then
return pairs
end
error("expected table", 3)
end
local iteratee = function(x)
if x == nil then return identity end
if iscallable(x) then return x end
if type(x) == "table" then
return function(z)
for k, v in pairs(x) do
if z[k] ~= v then return false end
end
return true
end
end
return function(z) return z[x] end
end
function lume.clamp(x, min, max)
return x < min and min or (x > max and max or x)
end
function lume.round(x, increment)
if increment then return lume.round(x / increment) * increment end
return x >= 0 and math_floor(x + .5) or math_ceil(x - .5)
end
function lume.sign(x)
return x < 0 and -1 or 1
end
function lume.lerp(a, b, amount)
return a + (b - a) * lume.clamp(amount, 0, 1)
end
function lume.smooth(a, b, amount)
local t = lume.clamp(amount, 0, 1)
local m = t * t * (3 - 2 * t)
return a + (b - a) * m
end
function lume.pingpong(x)
return 1 - math_abs(1 - x % 2)
end
function lume.distance(x1, y1, x2, y2, squared)
local dx = x1 - x2
local dy = y1 - y2
local s = dx * dx + dy * dy
return squared and s or math_sqrt(s)
end
function lume.angle(x1, y1, x2, y2)
return math_atan2(y2 - y1, x2 - x1)
end
function lume.vector(angle, magnitude)
return math.cos(angle) * magnitude, math.sin(angle) * magnitude
end
function lume.random(a, b)
if not a then a, b = 0, 1 end
if not b then b = 0 end
return a + math.random() * (b - a)
end
function lume.randomchoice(t)
return t[math.random(#t)]
end
function lume.weightedchoice(t)
local sum = 0
for _, v in pairs(t) do
assert(v >= 0, "weight value less than zero")
sum = sum + v
end
assert(sum ~= 0, "all weights are zero")
local rnd = lume.random(sum)
for k, v in pairs(t) do
if rnd < v then return k end
rnd = rnd - v
end
end
function lume.isarray(x)
return type(x) == "table" and x[1] ~= nil
end
function lume.push(t, ...)
local n = select("#", ...)
for i = 1, n do
t[#t + 1] = select(i, ...)
end
return ...
end
function lume.remove(t, x)
local iter = getiter(t)
for i, v in iter(t) do
if v == x then
if lume.isarray(t) then
table.remove(t, i)
break
else
t[i] = nil
break
end
end
end
return x
end
function lume.clear(t)
local iter = getiter(t)
for k in iter(t) do
t[k] = nil
end
return t
end
function lume.extend(t, ...)
for i = 1, select("#", ...) do
local x = select(i, ...)
if x then
for k, v in pairs(x) do
t[k] = v
end
end
end
return t
end
function lume.shuffle(t)
local rtn = {}
for i = 1, #t do
local r = math.random(i)
if r ~= i then
rtn[i] = rtn[r]
end
rtn[r] = t[i]
end
return rtn
end
function lume.sort(t, comp)
local rtn = lume.clone(t)
if comp then
if type(comp) == "string" then
table.sort(rtn, function(a, b) return a[comp] < b[comp] end)
else
table.sort(rtn, comp)
end
else
table.sort(rtn)
end
return rtn
end
function lume.array(...)
local t = {}
for x in ... do t[#t + 1] = x end
return t
end
function lume.each(t, fn, ...)
local iter = getiter(t)
if type(fn) == "string" then
for _, v in iter(t) do v[fn](v, ...) end
else
for _, v in iter(t) do fn(v, ...) end
end
return t
end
function lume.map(t, fn)
fn = iteratee(fn)
local iter = getiter(t)
local rtn = {}
for k, v in iter(t) do rtn[k] = fn(v) end
return rtn
end
function lume.all(t, fn)
fn = iteratee(fn)
local iter = getiter(t)
for _, v in iter(t) do
if not fn(v) then return false end
end
return true
end
function lume.any(t, fn)
fn = iteratee(fn)
local iter = getiter(t)
for _, v in iter(t) do
if fn(v) then return true end
end
return false
end
function lume.reduce(t, fn, first)
local acc = first
local started = first and true or false
local iter = getiter(t)
for _, v in iter(t) do
if started then
acc = fn(acc, v)
else
acc = v
started = true
end
end
assert(started, "reduce of an empty table with no first value")
return acc
end
function lume.unique(t)
local rtn = {}
for k in pairs(lume.invert(t)) do
rtn[#rtn + 1] = k
end
return rtn
end
function lume.filter(t, fn, retainkeys)
fn = iteratee(fn)
local iter = getiter(t)
local rtn = {}
if retainkeys then
for k, v in iter(t) do
if fn(v) then rtn[k] = v end
end
else
for _, v in iter(t) do
if fn(v) then rtn[#rtn + 1] = v end
end
end
return rtn
end
function lume.reject(t, fn, retainkeys)
fn = iteratee(fn)
local iter = getiter(t)
local rtn = {}
if retainkeys then
for k, v in iter(t) do
if not fn(v) then rtn[k] = v end
end
else
for _, v in iter(t) do
if not fn(v) then rtn[#rtn + 1] = v end
end
end
return rtn
end
function lume.merge(...)
local rtn = {}
for i = 1, select("#", ...) do
local t = select(i, ...)
local iter = getiter(t)
for k, v in iter(t) do
rtn[k] = v
end
end
return rtn
end
function lume.concat(...)
local rtn = {}
for i = 1, select("#", ...) do
local t = select(i, ...)
if t ~= nil then
local iter = getiter(t)
for _, v in iter(t) do
rtn[#rtn + 1] = v
end
end
end
return rtn
end
function lume.find(t, value)
local iter = getiter(t)
for k, v in iter(t) do
if v == value then return k end
end
return nil
end
function lume.match(t, fn)
fn = iteratee(fn)
local iter = getiter(t)
for k, v in iter(t) do
if fn(v) then return v, k end
end
return nil
end
function lume.count(t, fn)
local count = 0
local iter = getiter(t)
if fn then
fn = iteratee(fn)
for _, v in iter(t) do
if fn(v) then count = count + 1 end
end
else
if lume.isarray(t) then
return #t
end
for _ in iter(t) do count = count + 1 end
end
return count
end
function lume.slice(t, i, j)
i = i and absindex(#t, i) or 1
j = j and absindex(#t, j) or #t
local rtn = {}
for x = i < 1 and 1 or i, j > #t and #t or j do
rtn[#rtn + 1] = t[x]
end
return rtn
end
function lume.first(t, n)
if not n then return t[1] end
return lume.slice(t, 1, n)
end
function lume.last(t, n)
if not n then return t[#t] end
return lume.slice(t, -n, -1)
end
function lume.invert(t)
local rtn = {}
for k, v in pairs(t) do rtn[v] = k end
return rtn
end
function lume.pick(t, ...)
local rtn = {}
for i = 1, select("#", ...) do
local k = select(i, ...)
rtn[k] = t[k]
end
return rtn
end
function lume.keys(t)
local rtn = {}
local iter = getiter(t)
for k in iter(t) do rtn[#rtn + 1] = k end
return rtn
end
function lume.clone(t)
local rtn = {}
for k, v in pairs(t) do rtn[k] = v end
return rtn
end
function lume.fn(fn, ...)
assert(iscallable(fn), "expected a function as the first argument")
local args = { ... }
return function(...)
local a = lume.concat(args, { ... })
return fn(unpack(a))
end
end
function lume.once(fn, ...)
local f = lume.fn(fn, ...)
local done = false
return function(...)
if done then return end
done = true
return f(...)
end
end
local memoize_fnkey = {}
local memoize_nil = {}
function lume.memoize(fn)
local cache = {}
return function(...)
local c = cache
for i = 1, select("#", ...) do
local a = select(i, ...) or memoize_nil
c[a] = c[a] or {}
c = c[a]
end
c[memoize_fnkey] = c[memoize_fnkey] or {fn(...)}
return unpack(c[memoize_fnkey])
end
end
function lume.combine(...)
local n = select('#', ...)
if n == 0 then return noop end
if n == 1 then
local fn = select(1, ...)
if not fn then return noop end
assert(iscallable(fn), "expected a function or nil")
return fn
end
local funcs = {}
for i = 1, n do
local fn = select(i, ...)
if fn ~= nil then
assert(iscallable(fn), "expected a function or nil")
funcs[#funcs + 1] = fn
end
end
return function(...)
for _, f in ipairs(funcs) do f(...) end
end
end
function lume.call(fn, ...)
if fn then
return fn(...)
end
end
function lume.time(fn, ...)
local start = os.clock()
local rtn = {fn(...)}
return (os.clock() - start), unpack(rtn)
end
local lambda_cache = {}
function lume.lambda(str)
if not lambda_cache[str] then
local args, body = str:match([[^([%w,_ ]-)%->(.-)$]])
assert(args and body, "bad string lambda")
local s = "return function(" .. args .. ")\nreturn " .. body .. "\nend"
lambda_cache[str] = lume.dostring(s)
end
return lambda_cache[str]
end
local serialize
local serialize_map = {
[ "boolean" ] = tostring,
[ "nil" ] = tostring,
[ "string" ] = function(v) return string.format("%q", v) end,
[ "number" ] = function(v)
if v ~= v then return "0/0" -- nan
elseif v == 1 / 0 then return "1/0" -- inf
elseif v == -1 / 0 then return "-1/0" end -- -inf
return tostring(v)
end,
[ "table" ] = function(t, stk)
stk = stk or {}
if stk[t] then error("circular reference") end
local rtn = {}
stk[t] = true
for k, v in pairs(t) do
rtn[#rtn + 1] = "[" .. serialize(k, stk) .. "]=" .. serialize(v, stk)
end
stk[t] = nil
return "{" .. table.concat(rtn, ",") .. "}"
end
}
setmetatable(serialize_map, {
__index = function(_, k) error("unsupported serialize type: " .. k) end
})
serialize = function(x, stk)
return serialize_map[type(x)](x, stk)
end
function lume.serialize(x)
return serialize(x)
end
function lume.deserialize(str)
return lume.dostring("return " .. str)
end
function lume.split(str, sep)
if not sep then
return lume.array(str:gmatch("([%S]+)"))
else
assert(sep ~= "", "empty separator")
local psep = patternescape(sep)
return lume.array((str..sep):gmatch("(.-)("..psep..")"))
end
end
function lume.trim(str, chars)
if not chars then return str:match("^[%s]*(.-)[%s]*$") end
chars = patternescape(chars)
return str:match("^[" .. chars .. "]*(.-)[" .. chars .. "]*$")
end
function lume.wordwrap(str, limit)
limit = limit or 72
local check
if type(limit) == "number" then
check = function(s) return #s >= limit end
else
check = limit
end
local rtn = {}
local line = ""
for word, spaces in str:gmatch("(%S+)(%s*)") do
local s = line .. word
if check(s) then
table.insert(rtn, line .. "\n")
line = word
else
line = s
end
for c in spaces:gmatch(".") do
if c == "\n" then
table.insert(rtn, line .. "\n")
line = ""
else
line = line .. c
end
end
end
table.insert(rtn, line)
return table.concat(rtn)
end
function lume.format(str, vars)
if not vars then return str end
local f = function(x)
return tostring(vars[x] or vars[tonumber(x)] or "{" .. x .. "}")
end
return (str:gsub("{(.-)}", f))
end
function lume.trace(...)
local info = debug.getinfo(2, "Sl")
local t = { info.short_src .. ":" .. info.currentline .. ":" }
for i = 1, select("#", ...) do
local x = select(i, ...)
if type(x) == "number" then
x = string.format("%g", lume.round(x, .01))
end
t[#t + 1] = tostring(x)
end
print(table.concat(t, " "))
end
function lume.dostring(str)
return assert((loadstring or load)(str))()
end
function lume.uuid()
local fn = function(x)
local r = math.random(16) - 1
r = (x == "x") and (r + 1) or (r % 4) + 9
return ("0123456789abcdef"):sub(r, r)
end
return (("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"):gsub("[xy]", fn))
end
function lume.hotswap(modname)
local oldglobal = lume.clone(_G)
local updated = {}
local function update(old, new)
if updated[old] then return end
updated[old] = true
local oldmt, newmt = getmetatable(old), getmetatable(new)
if oldmt and newmt then update(oldmt, newmt) end
for k, v in pairs(new) do
if type(v) == "table" then update(old[k], v) else old[k] = v end
end
end
local err = nil
local function onerror(e)
for k in pairs(_G) do _G[k] = oldglobal[k] end
err = lume.trim(e)
end
local ok, oldmod = pcall(require, modname)
oldmod = ok and oldmod or nil
xpcall(function()
package.loaded[modname] = nil
local newmod = require(modname)
if type(oldmod) == "table" then update(oldmod, newmod) end
for k, v in pairs(oldglobal) do
if v ~= _G[k] and type(v) == "table" then
update(v, _G[k])
_G[k] = v
end
end
end, onerror)
package.loaded[modname] = oldmod
if err then return nil, err end
return oldmod
end
local ripairs_iter = function(t, i)
i = i - 1
local v = t[i]
if v ~= nil then
return i, v
end
end
function lume.ripairs(t)
return ripairs_iter, t, (#t + 1)
end
function lume.color(str, mul)
mul = mul or 1
local r, g, b, a
r, g, b = str:match("#(%x%x)(%x%x)(%x%x)")
if r then
r = tonumber(r, 16) / 0xff
g = tonumber(g, 16) / 0xff
b = tonumber(b, 16) / 0xff
a = 1
elseif str:match("rgba?%s*%([%d%s%.,]+%)") then
local f = str:gmatch("[%d.]+")
r = (f() or 0) / 0xff
g = (f() or 0) / 0xff
b = (f() or 0) / 0xff
a = f() or 1
else
error(("bad color string '%s'"):format(str))
end
return r * mul, g * mul, b * mul, a * mul
end
local chain_mt = {}
chain_mt.__index = lume.map(lume.filter(lume, iscallable, true),
function(fn)
return function(self, ...)
self._value = fn(self._value, ...)
return self
end
end)
chain_mt.__index.result = function(x) return x._value end
function lume.chain(value)
return setmetatable({ _value = value }, chain_mt)
end
setmetatable(lume, {
__call = function(_, ...)
return lume.chain(...)
end
})
return lume

51
lib/stdio.fnl Normal file
View file

@ -0,0 +1,51 @@
(local fennel (require :lib.fennel))
(require :love.event)
;; This module exists in order to expose stdio over a channel so that it
;; can be used in a non-blocking way from another thread.
(fn prompt [cont?]
(io.write (if cont? ".." ">> ")) (io.flush) (.. (tostring (io.read)) "\n"))
;; This module is loaded twice: initially in the main thread where ... is nil,
;; and then again in a separate thread where ... contains the channel used to
;; communicate with the main thread.
(fn looper [event channel]
(match (channel:demand)
[:write vals] (do (io.write (table.concat vals "\t"))
(io.write "\n"))
[:read cont?] (love.event.push event (prompt cont?)))
(looper event channel))
(match ...
(event channel) (looper event channel))
{:start (fn start-repl []
(let [code (love.filesystem.read "lib/stdio.fnl")
luac (if code
(love.filesystem.newFileData
(fennel.compileString code) "io")
(love.filesystem.read "lib/stdio.lua"))
thread (love.thread.newThread luac)
io-channel (love.thread.newChannel)
;; fennel 1.5 has updated repl to be a table
;; with a metatable __call value. coroutine.create
;; does not reference the metatable __call value
;; by applying partial we fix this issue in a
;; backwards compatible way.
coro (coroutine.create (partial fennel.repl))
options {:readChunk (fn [{: stack-size}]
(io-channel:push [:read (< 0 stack-size)])
(coroutine.yield))
:onValues (fn [vals]
(io-channel:push [:write vals]))
:onError (fn [errtype err]
(io-channel:push [:write [err]]))
:moduleName "lib.fennel"}]
;; this thread will send "eval" events for us to consume:
(coroutine.resume coro options)
(thread:start "eval" io-channel)
(set love.handlers.eval
(fn [input]
(coroutine.resume coro input)))))}

76
lib/stdio.lua Normal file
View file

@ -0,0 +1,76 @@
local fennel = require("lib.fennel")
require("love.event")
local function prompt(cont_3f)
local function _1_()
if cont_3f then
return ".."
else
return ">> "
end
end
io.write(_1_())
io.flush()
return (tostring(io.read()) .. "\n")
end
local function looper(event, channel)
do
local _2_ = channel:demand()
if ((_G.type(_2_) == "table") and (_2_[1] == "write") and (nil ~= _2_[2])) then
local vals = _2_[2]
io.write(table.concat(vals, "\9"))
io.write("\n")
elseif ((_G.type(_2_) == "table") and (_2_[1] == "read") and (nil ~= _2_[2])) then
local cont_3f = _2_[2]
love.event.push(event, prompt(cont_3f))
else
end
end
return looper(event, channel)
end
do
local _4_, _5_ = ...
if ((nil ~= _4_) and (nil ~= _5_)) then
local event = _4_
local channel = _5_
looper(event, channel)
else
end
end
local function start_repl()
local code = love.filesystem.read("lib/stdio.fnl")
local luac
if code then
luac = love.filesystem.newFileData(fennel.compileString(code), "io")
else
luac = love.filesystem.read("lib/stdio.lua")
end
local thread = love.thread.newThread(luac)
local io_channel = love.thread.newChannel()
local coro
local function _8_(...)
return fennel.repl(...)
end
coro = coroutine.create(_8_)
local options
local function _11_(_9_)
local _arg_10_ = _9_
local stack_size = _arg_10_["stack-size"]
io_channel:push({"read", (0 < stack_size)})
return coroutine.yield()
end
local function _12_(vals)
return io_channel:push({"write", vals})
end
local function _13_(errtype, err)
return io_channel:push({"write", {err}})
end
options = {readChunk = _11_, onValues = _12_, onError = _13_, moduleName = "lib.fennel"}
coroutine.resume(coro, options)
thread:start("eval", io_channel)
local function _14_(input)
return coroutine.resume(coro, input)
end
love.handlers.eval = _14_
return nil
end
return {start = start_repl}

674
license.txt Normal file
View file

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/philosophy/why-not-lgpl.html>.

21
main.lua Normal file
View file

@ -0,0 +1,21 @@
-- bootstrap the compiler
local fennel = require("lib.fennel").install({correlate=true,
moduleName="lib.fennel"})
local make_love_searcher = function(env)
return function(module_name)
local path = module_name:gsub("%.", "/") .. ".fnl"
if love.filesystem.getInfo(path) then
return function(...)
local code = love.filesystem.read(path)
return fennel.eval(code, {env=env}, ...)
end, path
end
end
end
table.insert(package.loaders, make_love_searcher(_G))
table.insert(fennel["macro-searchers"], make_love_searcher("_COMPILER"))
require("wrap")

72
makefile Normal file
View file

@ -0,0 +1,72 @@
VERSION=0.1.0
LOVE_VERSION=11.5
NAME=escape-the-crash
ITCH_ACCOUNT=vagabondazulien
URL=https://forge.niblock.tech/vagabond/escape-the-crash
AUTHOR="Bill Niblock"
DESCRIPTION="Navigate a forsaken city to the evacuation tower and escape certain doom!"
GITHUB_USERNAME := $(shell grep GITHUB_USERNAME credentials.private | cut -d= -f2)
GITHUB_PAT := $(shell grep GITHUB_PAT credentials.private | cut -d= -f2)
LIBS := $(wildcard lib/*)
LUA := $(wildcard *.lua)
SRC := $(wildcard *.fnl)
run: ; love .
count: ; cloc *.fnl
clean: ; rm -rf releases/*
LOVEFILE=releases/$(NAME)-$(VERSION).love
$(LOVEFILE): $(LUA) $(SRC) $(LIBS) assets
mkdir -p releases/
find $^ -type f | LC_ALL=C sort | env TZ=UTC zip -r -q -9 -X $@ -@
love: $(LOVEFILE)
# platform-specific distributables
REL=$(PWD)/buildtools/love-release.sh # https://p.hagelb.org/love-release.sh
FLAGS=-a "$(AUTHOR)" --description $(DESCRIPTION) \
--love $(LOVE_VERSION) --url $(URL) --version $(VERSION) --lovefile $(LOVEFILE)
releases/$(NAME)-$(VERSION)-x86_64.AppImage: $(LOVEFILE)
cd buildtools/appimage && \
./build.sh $(LOVE_VERSION) $(PWD)/$(LOVEFILE) $(GITHUB_USERNAME) $(GITHUB_PAT)
mv buildtools/appimage/game-x86_64.AppImage $@
releases/$(NAME)-$(VERSION)-macos.zip: $(LOVEFILE)
$(REL) $(FLAGS) -M
mv releases/$(NAME)-macos.zip $@
releases/$(NAME)-$(VERSION)-win.zip: $(LOVEFILE)
$(REL) $(FLAGS) -W32
mv releases/$(NAME)-win32.zip $@
releases/$(NAME)-$(VERSION)-web.zip: $(LOVEFILE)
buildtools/love-js/love-js.sh releases/$(NAME)-$(VERSION).love $(NAME) -v=$(VERSION) -a=$(AUTHOR) -o=releases
linux: releases/$(NAME)-$(VERSION)-x86_64.AppImage
mac: releases/$(NAME)-$(VERSION)-macos.zip
windows: releases/$(NAME)-$(VERSION)-win.zip
web: releases/$(NAME)-$(VERSION)-web.zip
runweb: $(LOVEFILE)
buildtools/love-js/love-js.sh $(LOVEFILE) $(NAME) -v=$(VERSION) -a=$(AUTHOR) -o=releases -r -n
# If you release on itch.io, you should install butler:
# https://itch.io/docs/butler/installing.html
uploadlinux: releases/$(NAME)-$(VERSION)-x86_64.AppImage
butler push $^ $(ITCH_ACCOUNT)/$(NAME):linux --userversion $(VERSION)
uploadmac: releases/$(NAME)-$(VERSION)-macos.zip
butler push $^ $(ITCH_ACCOUNT)/$(NAME):mac --userversion $(VERSION)
uploadwindows: releases/$(NAME)-$(VERSION)-win.zip
butler push $^ $(ITCH_ACCOUNT)/$(NAME):windows --userversion $(VERSION)
uploadweb: releases/$(NAME)-$(VERSION)-web.zip
butler push $^ $(ITCH_ACCOUNT)/$(NAME):web --userversion $(VERSION)
upload: uploadlinux uploadmac uploadwindows uploadweb
release: linux mac windows web upload clean

46
mapper.fnl Normal file
View file

@ -0,0 +1,46 @@
; Cell Stuff
; This will result in a "2x2" cell, of [0 1 1 1], with 0 representing an empty
; space, and 1 representing a wall.
(var (hall-width wall-width) (values 1 2))
; This sets the "map width". This means 10 cells across and down. With each cell
; being 2x2, this results in a 20x20 square map. All maps will be square (for
; now).
(var cell-num 7)
; Explicitly setting cell-size for convenience. All cells are squares
(var cell-size (+ hall-width wall-width))
; Initialize the cell table
(var cells {})
(var cell [])
(for [c 1 (* cell-num cell-num)]
(for [i 1 cell-size]
(for [j 1 cell-size]
(var cell-index (+ j (* (- i 1) cell-size)))
(if (and (< i (+ hall-width 1)) (< j (+ hall-width 1)))
(tset cell cell-index 0)
(tset cell cell-index 1))))
(table.insert cells cell))
(fn map-row [i] (+ 1 (// (- i 1) cell-num)))
(fn map-index [i] (+ (- (* i cell-size) 1) (* cell-size cell-num (- (map-row i) 1))))
(fn generate_cell_map [cells]
(var cell_map [])
(for [c 1 (length cells)]
(var (mi ci) (values (map-index c) 1))
(for [i 1 cell-size]
(for [j 1 cell-size]
(var new-map-index (+ mi (+ (- i 1) (* (- j 1) (* cell-num cell-size)))))
(var new-cell-index (+ ci (+ (- i 1) (* (- j 1) cell-size))))
(tset cell_map new-map-index (. cells c new-cell-index)))))
cell_map)
(fn print_cell_map [cells]
(var output "")
(for [i 1 (length cells)]
(set output (.. output " " (. cells i)))
(if (= 0 (% i (* cell-num cell-size))) (set output (.. output "\n"))))
(print output))
(print_cell_map (generate_cell_map cells))

114
mode-intro.fnl Normal file
View file

@ -0,0 +1,114 @@
(local overlay (require :overlay))
(local state (require :state))
(local mapper (require :mapper))
(love.graphics.setNewFont 10)
(local pi math.pi)
(var map (mapper.generate 10 10))
; This sets the wall texture data
(var walls [])
(var wall-textures (love.filesystem.getDirectoryItems "textures/walls"))
(each [_ v (ipairs wall-textures)]
(local wall {})
(tset wall :t (love.graphics.newImage (.. "textures/walls/" v)))
(tset wall :w (love.graphics.getWidth (. wall :t)))
(tset wall :h (love.graphics.getHeight (. wall :t)))
(table.insert walls wall))
; Map size
(var map-height (length map))
(var map-width (length (. map 1)))
; Player position and direction
(var player (state.getPlayer))
; Screen size
(var (screen-width screen-height) (love.window.getMode))
; (var screen-width 1280)
; (var screen-height 720)
(var texel-width 64)
(var texel-height 64)
; Ray-casting function
(fn cast-ray [ray-angle]
(local dx (math.cos ray-angle))
(local dy (math.sin ray-angle))
(var rays [])
(var (distance tex done hit-pos) (values 0 1 false {}))
(while (not done)
(var ray-x (+ player.x (* dx distance)))
(var ray-y (+ player.y (* dy distance)))
(set rays [ray-x ray-y])
; Check if ray hits a wall (>= 1) on the map
(if (or (>= (math.floor ray-x) map-width)
(>= (math.floor ray-y) map-height)
(<= ray-x 0)
(<= ray-y 0)
(>= (. map (math.floor ray-y) (math.floor ray-x)) 1))
(do
(set done true)
(set tex (. map (math.floor ray-y) (math.floor ray-x)))
(set hit-pos {:x (- ray-x (math.floor ray-x)) :y (- ray-y (math.floor ray-y))}))
; Increment distance
(set distance (+ distance 0.01))))
(values distance hit-pos tex rays))
; Function to handle player movement
(fn move-player [move-speed]
(let [new-x (+ player.x (* (math.cos player.d) move-speed))
new-y (+ player.y (* (math.sin player.d) move-speed))]
; Check for collisions with walls
(when (= (. map (math.floor new-y) (math.floor new-x)) 0)
(state.modO -1) (state.setX new-x) (state.setY new-y))))
; Draw function for rendering
{:draw (fn love.draw []
(love.graphics.clear)
; For each vertical slice of the screen
(var (last-x last-y) (values 0 0))
(for [i 0 (- screen-width 1)]
; Calculate angle of ray relative to player direction
(local ray-angle (+ player.d (- (* (/ i screen-width) player.f) (/ player.f 2))))
; Cast the ray to find distance to the nearest wall
(local (distance hit-pos tex rays) (values (cast-ray ray-angle)))
; Calculate height of the wall slice
(local wall-height (math.floor (/ screen-height distance)))
(local start-y (/ (- screen-height wall-height) 2))
(local end-y (/ (+ screen-height wall-height) 2))
; Draw a textured wall
(local wall-texture (. walls tex))
(var texture-x 0)
(if (> (/ (- (. rays 1) last-x) (- (. rays 2) last-y)) (/ (- (. rays 2) last-y) (- (. rays 1) last-x)))
(set texture-x (math.floor (* (. hit-pos :y) (. wall-texture :w))))
(set texture-x (math.floor (* (. hit-pos :x) (. wall-texture :w)))))
(love.graphics.draw (. wall-texture :t)
(love.graphics.newQuad texture-x 0 1 (. wall-texture :h) (. wall-texture :w) (. wall-texture :h))
i start-y 0 1 (/ wall-height (. wall-texture :h)))
(when (= 0 (% i 200))
(love.graphics.print (.. "r: " ray-angle) i 10)
(love.graphics.print (.. "h-x: " (. rays 1)) i 20)
(love.graphics.print (.. "h-y: " (. rays 2)) i 30)
)
(set (last-x last-y) (values (. rays 1) (. rays 2)))
)
(overlay.overlay)
(set player (state.getPlayer))
)
:keypressed (fn keypressed [key set-mode]
(when (= key "j") (state.setD (- player.d (/ pi 2))))
(when (= key "l") (state.setD (+ player.d (/ pi 2))))
(when (= key "i") (move-player 1))
(when (= key "k") (move-player -1))
(when (= key "x") (love.event.quit)))}

127
notes.md Normal file
View file

@ -0,0 +1,127 @@
First, establish some of the cell data
```lisp
; This will result in a "2x2" cell, of [0 1 1 1], with 0 representing an empty
; space, and 1 representing a wall.
(var (hall-width wall-width) (values 1 1))
; This sets the "map width". This means 10 cells across and down. With each cell
; being 2x2, this results in a 20x20 square map. All maps will be square (for
; now).
(var num-cells 10)
; Explicitly setting cell-size for convenience. All cells are squares
(var cell-size (+ hall-width wall-width))
```
Next, generate the initial array of cells to work with.
```lisp
(var cells {})
(for [i 1 (* num-cells num-cells)]
(table.insert cells {
:n false :s false :e false :w false
:v false
:o false
}))
```
`:n`, `:s`, `:e`, `:w` - if cell has a directional connection
`:v` - has the cell been visited
`:o` - does the cell contain an obstacle
With the cells established, can use a recursive depth-first search with a stack
to generate the maze.
For ease, we'll always start along the top. Eventually, want to ensure the
solution is along the sides or bottom.
```lisp
(var cell-stack [(math.random 1 num-cells)])
(while (> 0 (length cell-stack)
(var current-cell (table.remove cell-stack (length cell-stack)))
(tset cells current-cell :v true)
; Check if any of the current cell's neighbors are
; 1. unvisited
; 2. a side/barrier wall
(var next-cells {})
; North -
; if current-cell <= num-cells, then north is a map-side/barrier
(when (> num-cells current-cell)
(var n-cell (- current-cell num-cells))
(if (not (. cells n-cell :v)) (table.insert next-cells n-cell)))
; South -
; if current-cell > (- (* num-cells num-cells) num-cells),
; then south is a map-side/barrier
(when (< current-cell (- (* num-cells num-cells) num-cells))
(var s-cell (- (* num-cells num-cells) num-cells))
(if (not (. cells s-cell :v)) (table.insert next-cells s-cell)))
; East -
; if current-cell % num-cells = 0, then east is a map-side/barrier
(when (not (= 0 (% current-cell num-cells)))
(var e-cell (+ current-cell 1))
(if (not (. cells e-cell :v)) (table.insert next-cells e-cell)))
; West -
; if current-cell % num-cells = 1, then west is a map-side/barrier
(when (not (= 1 (% current-cell num-cells)))
(var w-cell (- current-cell 1))
(if (not (. cells e-cell :v)) (table.insert next-cells w-cell)))
```
---
For output:
Each cell is then used to generate values in the map array. Each cell will have
either a hallway or a wall at each position, for `cell-size` positions.
Iterate through each cell. Cells 1 through `num-cells` are the first row, then
(1 + `num-cells`) through (`num-cells` * 2), and so on. This can be generalized
and extrapolated to `(x + (num-cells * (y - 1)))` through `(num-cells * y)`, for
`x` and `y` loops from 1 to `num-cells`.
```lisp
(for [i 1 num-cells]
(for [j 1 num-cells]
```
Each cell is of uniform size, and the map is a square that divides evenly by
that size. We established the width, and that value squared is the map. Every
cell is numbered from 1 to `(* num-cells num-cells)` (ie., 1 to 100). The index
of the cell divided by `num-cells`, plus 1, gets the row:
```lisp
(fn row [i] (+ 1 (// i num-cells)))
```
This gets the "upper-left" for the map array, given a cell index.
```lisp
(fn c [i] (+ (- (* i cell-size) 1) (* cell-size cell-num (- (row i) 1))))
```
Using that index, iterate through the cell based on `cell-size` twice, using
each loop to add either `(- i 1)` and `(* (- j 1) cell-num)` to the value from
`c` above, and insert it into the map array.
---
For cell generation:
Each cell is a combination of hallways and walls. For each cell, if the "row" or
"column" is less than the `hall-width`, enter a "0", otherwise enter a "1".
```lisp
(var cell [])
(for [i 1 cell-size]
(for [j 1 cell-size]
(var cell-index (+ j (* (- i 1) cell-size)))
(if (and (< i (+ hall-width 1)) (< j (+ hall-width 1)))
(tset cell cell-index 0)
(tset cell cell-index 1))))
```

64
overlay.fnl Normal file
View file

@ -0,0 +1,64 @@
; Draw the overlay
; Relies on information about the player and the display
(local player (require :state))
(var screen-width 1280)
(var screen-height 720)
; This draws the oxygen ui
; Sequential blocks, up to 10; each block 10%
(fn oxygen-ui [x y]
(local (bx by bb o) (values 10 10 15 (player.getO)))
(local bn (math.floor (/ o 10)))
(var (dx dy) (values 0 50))
(local bp (* 5 (% o 10)))
(love.graphics.setColor 0 1 0 0.25)
(for [i 1 10]
(love.graphics.polygon "line" (+ x dx 1) (+ y by 1)
(+ x dx bx 1) (+ y by 1)
(+ x dx bx 1) (+ y by dy 1)
(+ x dx 1) (+ y by dy 1))
(if (<= i bn)
(love.graphics.polygon "fill" (+ x dx) (+ y by)
(+ x dx bx) (+ y by)
(+ x dx bx) (+ y by dy)
(+ x dx) (+ y by dy)))
(if (= i (+ bn 1))
(love.graphics.polygon "fill" (+ x dx) (+ y (- (+ dy by) bp))
(+ x dx bx) (+ y (- (+ dy by) bp))
(+ x dx bx) (+ y by dy)
(+ x dx) (+ y by dy)))
(set dx (+ dx bb))))
; This draws the power ui
; Inverse-sequential blocks, up to 10; each block 10%
(fn power-ui [x y]
(local (bx by bb p) (values 10 10 15 (player.getP)))
(local bn (math.floor (/ p 10)))
(var (dx dy) (values 0 50))
(local bp (* 5 (% p 10)))
(love.graphics.setColor 1 1 0 0.25)
(for [i 1 10]
(love.graphics.polygon "line" (- x dx 1) (+ y by 1)
(- x dx bx 1) (+ y by 1)
(- x dx bx 1) (+ y by dy 1)
(- x dx 1) (+ y by dy 1))
(if (<= i bn)
(love.graphics.polygon "fill" (- x dx) (+ y by)
(- x dx bx) (+ y by)
(- x dx bx) (+ y by dy)
(- x dx) (+ y by dy)))
(if (= i (+ bn 1))
(love.graphics.polygon "fill" (- x dx) (+ y (- (+ dy by) bp))
(- x dx bx) (+ y (- (+ dy by) bp))
(- x dx bx) (+ y by dy)
(- x dx) (+ y by dy)))
(set dx (+ dx bb))))
(fn overlay [dx dy]
(love.graphics.translate dx (- dy))
(oxygen-ui (+ (/ screen-width 2) 100) (- screen-height 100))
(power-ui (- (/ screen-width 2) 100) (- screen-height 100))
(love.graphics.setColor 1 1 1)
)
{: overlay}

307
raycaster.fnl Normal file
View file

@ -0,0 +1,307 @@
(local state (require :state))
(local mapper (require :mapper))
(local overlay (require :overlay))
(local pi math.pi)
; ### Screen Size ###
(var (screen-width screen-height) (love.window.getMode))
; ### Map Information ###
; (var map (mapper.generate 15 15))
(var map [[1 1 2 1 1 1 1 2 1 1 1 ]
[1 0 0 0 0 0 0 0 0 0 1 ]
[1 0 0 0 0 0 0 0 0 0 2 ]
[2 0 0 0 0 0 0 0 0 0 2 ]
[1 0 0 0 0 0 0 0 0 0 1 ]
[1 0 0 0 0 0 0 0 0 0 1 ]
[1 0 0 0 0 0 0 0 3 0 1 ]
[1 0 0 0 0 0 0 3 3 0 1 ]
[1 0 0 0 0 0 0 0 0 0 1 ]
[1 1 1 1 1 1 1 1 1 1 1 ]])
; ### Texture Information ###
(var walls [])
(var wall-textures (love.filesystem.getDirectoryItems "assets/textures/walls"))
(var (tex-height tex-width) (values 64 64))
(var skybox [])
(var skybox-textures (love.filesystem.getDirectoryItems "assets/textures/skybox"))
(each [_ v (ipairs wall-textures)]
(local wall {})
(tset wall :t (love.graphics.newImage (.. "assets/textures/walls/" v)))
(tset wall :w (love.graphics.getWidth (. wall :t)))
(tset wall :h (love.graphics.getHeight (. wall :t)))
(table.insert walls wall))
(each [_ v (ipairs skybox-textures)]
(local skyb {})
(tset skyb :t (love.graphics.newImage (.. "assets/textures/skybox/" v)))
(tset skyb :w (love.graphics.getWidth (. skyb :t)))
(tset skyb :h (love.graphics.getHeight (. skyb :t)))
(table.insert skybox skyb))
; ### "Player" variables ###
(var (posx posy) (values 4.0 4.0)) ; Initial map position
(var (dirx diry) (values -1.0 0.0)) ; Initial direction vector
(var (planex planey) (values 0 0.66)) ; Camera plane
; ### Offset experimentation ###
(var (offst-x offst-y) (values 0 0))
{
:draw (fn love.draw []
; Mouse-Look
(love.graphics.translate offst-x offst-y)
; "Skybox"
; Draw a big thing before everything else
; (love.graphics.draw (. skybox 1 :t) -20 -20)
; CEILING/FLOOR CASTING
; (var cf-f-tex-num (. walls 7))
; (var cf-c-tex-num (. walls 6))
; (var floor-texel (love.graphics.newQuad 0 0 1 1 (. cf-f-tex-num :w) (. cf-f-tex-num :h)))
; (for [i (/ screen-height 2) screen-height]
; ; Set ray-dir for left-most (i = 0) and right-most (i = screen-width) rays
; (var (cf-ray-dir-x0 cf-ray-dir-y0) (values (- dirx planex) (- diry planey)))
; (var (cf-ray-dir-x1 cf-ray-dir-y1) (values (+ dirx planex) (+ diry planey)))
; ; Current y position compared to horizon
; (var cf-p (math.floor (/ (- i screen-height) 2)))
; ; Vertical position of the camera
; (var cf-pos-z (* 0.5 screen-height))
; ; Horizontal distance from camera to floor for current row
; (var cf-row-distance (/ cf-pos-z cf-p))
; ; Calculate step vectors
; (var cf-floor-step-x (/ (* cf-row-distance (- cf-ray-dir-x1 cf-ray-dir-x0)) screen-width))
; (var cf-floor-step-y (/ (* cf-row-distance (- cf-ray-dir-y1 cf-ray-dir-y0)) screen-width))
; ; Coordinates of left-most column, updated stepping to the right
; (var (cf-floor-x cf-floor-y) (values (+ posx (* cf-row-distance cf-ray-dir-x0))
; (+ posy (* cf-row-distance cf-ray-dir-y0))))
; ; Draw floor and ceiling
; (for [j 0 screen-width]
; ; Get cell
; (var (cf-cell-x cf-cell-y) (values (math.floor cf-floor-x) (math.floor cf-floor-y)))
; ; Get texture coordinate from fractional part
; ; CPP Code: wtf is the & doing there?
; ; (var (cf-tx cf-ty) (values (math.floor (% (* tex-width (- cf-floor-x cf-cell-x)) tex-width))
; ; (math.floor (% (* tex-height (- cf-floor-y cf-cell-y)) tex-height))))
; (var (cf-tx cf-ty) (values (math.floor (* tex-width (- cf-floor-x cf-cell-x)))
; (math.floor (* tex-height (- cf-floor-y cf-cell-y)))))
; ;; Draw the texture
; ; (love.graphics.draw (. tex-num :t)
; ; (love.graphics.newQuad tex-x 0 1 (. tex-num :h) (. tex-num :w) (. tex-num :h))
; ; i draw-start 0 1 (/ line-height (. tex-num :h)))
; ; (love.graphics.draw (. cf-f-tex-num :t)
; ; floor-texel
; ; j i 0 1 1)
; ; Step
; (set cf-floor-x (+ cf-floor-x cf-floor-step-x))
; (set cf-floor-y (+ cf-floor-y cf-floor-step-y))
; )
; )
; WALL CASTING
(for [i 0 screen-width]
; Calculate ray position and direction
; Originals, giving a fish-eye lens effect:
; (var camerax (/ (* 2 i) (- screen-width 1)))
; (var (ray-dir-x ray-dir-y) (values (+ dirx (* planex camerax)) (+ diry (* planey camerax))))
(var camerax (- (/ (* 2 i) (- screen-width 1)) 1.0))
(var (ray-dir-x ray-dir-y) (values (+ dirx (* planex camerax)) (+ diry (* planey camerax))))
; Which map square we're in
(var (mapx mapy) (values (math.floor posx) (math.floor posy)))
; Length of ray from current position to next x or y side
(var (side-dist-x side-dist-y) (values 0 0))
; Length of ray from one x or y side to the next x or y side
; (var (delta-dist-x delta-dist-y) (values
; (math.sqrt (+ 1 (/ (* ray-dir-y ray-dir-y) (* ray-dir-x ray-dir-x))))
; (math.sqrt (+ 1 (/ (* ray-dir-x ray-dir-x) (* ray-dir-y ray-dir-y))))))
; (var (delta-dist-x delta-dist-y) (values
; (math.sqrt (+ 1 (* (/ ray-dir-y ray-dir-x) (/ ray-dir-y ray-dir-x))))
; (math.sqrt (+ 1 (* (/ ray-dir-x ray-dir-y) (/ ray-dir-x ray-dir-y))))))
(var (delta-dist-x delta-dist-y) (values (math.abs (/ 1 ray-dir-x)) (math.abs (/ 1 ray-dir-y))))
(var perp-wall-dist 0)
; Direction to step in, x or y
(var (stepx stepy) (values 0 0))
; Side the ray hits, x (0) or y (1)
(var side 0)
; Calculate step and initial side-dist-*
(if (< ray-dir-x 0)
(do
(set stepx -1)
(set side-dist-x (* (- posx mapx) delta-dist-x)))
(do
(set stepx 1)
(set side-dist-x (* (- (+ mapx 1.0) posx) delta-dist-x))))
(if (< ray-dir-y 0)
(do
(set stepy -1)
(set side-dist-y (* (- posy mapy) delta-dist-y)))
(do
(set stepy 1)
(set side-dist-y (* (- (+ mapy 1.0) posy) delta-dist-y))))
; Perform DDA (Digital Differential Analysis)
(var hit false)
(while (not hit)
(if (< side-dist-x side-dist-y)
(do
(set side-dist-x (+ side-dist-x delta-dist-x))
(set mapx (+ mapx stepx))
(set side 0))
(do
(set side-dist-y (+ side-dist-y delta-dist-y))
(set mapy (+ mapy stepy))
(set side 1)))
(if (> (. map mapx mapy) 0) (set hit true)))
; Calculate distance of perpendicular ray, to avoid fisheye effect
(if (= side 0)
(set perp-wall-dist (- side-dist-x delta-dist-x))
(set perp-wall-dist (- side-dist-y delta-dist-y)))
; Calculate height of line to draw on screen
(var line-height (/ screen-height perp-wall-dist))
; Calculate lowest and highest pixel to fill in current stripe
(var draw-start (+ (/ (- line-height) 2) (/ screen-height 2)))
(if (< draw-start 0) (set draw-start 0))
(var draw-end (+ (/ line-height 2) (/ screen-height 2)))
(if (>= draw-end screen-height) (set draw-end (- screen-height 1)))
; Draw textured wall
;; Choose texture
(var tex-num (. walls (. map mapx mapy)))
;; Calculate exactly where the wall was hit
(var wallx 0)
(if (= side 0)
(set wallx (+ posy (* perp-wall-dist ray-dir-y)))
(set wallx (+ posx (* perp-wall-dist ray-dir-x))))
(set wallx (- wallx (math.floor wallx)))
;; Find the x-coordinate on the texture
(var tex-x (math.floor (* wallx (. tex-num :w))))
(if (and (= side 0) (> ray-dir-x 0)) (set tex-x (- (. tex-num :w) tex-x 1)))
(if (and (= side 1) (< ray-dir-y 0)) (set tex-x (- (. tex-num :w) tex-x 1)))
;; Draw the texture, accounting for "fog"
(var fog-dist (- 1 (/ perp-wall-dist 7)))
(love.graphics.setColor 1 1 1 fog-dist)
(love.graphics.draw (. tex-num :t)
(love.graphics.newQuad tex-x 0 1 (. tex-num :h) (. tex-num :w) (. tex-num :h))
i draw-start 0 1 (/ line-height (. tex-num :h)))
(when (= (. map mapx mapy) 3)
(for [q 1 3]
(set fog-dist (- fog-dist 0.1))
(love.graphics.setColor 1 1 1 fog-dist)
(love.graphics.draw (. tex-num :t)
(love.graphics.newQuad tex-x 0 1 (. tex-num :h) (. tex-num :w) (. tex-num :h))
i (- draw-start (* line-height q)) 0 1 (/ line-height (. tex-num :h)))
)
)
)
(overlay.overlay offst-x offst-y)
)
:update (fn update [dt]
(var mvspeed (* dt 3.0))
(var rtspeed (* dt 1.0))
(when (love.keyboard.isDown "e")
(when (= 0 (. map (math.floor (+ (* mvspeed dirx) posx)) (math.floor posy)))
(set posx (+ (* mvspeed dirx) posx)))
(when (= 0 (. map (math.floor posx) (math.floor (+ (* mvspeed diry) posy))))
(set posy (+ (* mvspeed diry) posy)))
)
(when (love.keyboard.isDown "d")
(when (= 0 (. map (math.floor (- posx (* mvspeed dirx))) (math.floor posy)))
(set posx (- posx (* mvspeed dirx))))
(when (= 0 (. map (math.floor posx) (math.floor (- posy (* mvspeed diry)))))
(set posy (- posy (* mvspeed diry))))
)
(when (love.keyboard.isDown "f")
(when (= 0 (. map (math.floor (+ (* mvspeed planex) posx)) (math.floor posy)))
(set posx (+ (* mvspeed planex) posx)))
(when (= 0 (. map (math.floor posx) (math.floor (+ (* mvspeed planey) posy))))
(set posy (+ (* mvspeed planey) posy)))
)
(when (love.keyboard.isDown "s")
(when (= 0 (. map (math.floor (- posx (* mvspeed planex))) (math.floor posy)))
(set posx (- posx (* mvspeed planex))))
(when (= 0 (. map (math.floor posx) (math.floor (- posy (* mvspeed planey)))))
(set posy (- posy (* mvspeed planey))))
)
; (when (love.keyboard.isDown "s")
; (var strafe-x (- (* dirx (math.cos (- (/ math.pi 2)))) (* diry (math.sin (- (/ math.pi 2))))))
; (var strafe-y (+ (* dirx (math.sin (- (/ math.pi 2)))) (* diry (math.cos (- (/ math.pi 2))))))
; (when (= 0 (. map (math.floor (- posx (* mvspeed strafe-x))) (math.floor posy)))
; (set posx (- posx (* mvspeed strafe-x))))
; (when (= 0 (. map (math.floor posx) (math.floor (- posy (* mvspeed strafe-y)))))
; (set posy (- posy (* mvspeed strafe-y))))
; )
; (when (love.keyboard.isDown "f")
; (var strafe-x (- (* dirx (math.cos (+ (/ math.pi 2)))) (* diry (math.sin (+ (/ math.pi 2))))))
; (var strafe-y (+ (* dirx (math.sin (+ (/ math.pi 2)))) (* diry (math.cos (+ (/ math.pi 2))))))
; (when (= 0 (. map (math.floor (- posx (* mvspeed strafe-x))) (math.floor posy)))
; (set posx (- posx (* mvspeed strafe-x))))
; (when (= 0 (. map (math.floor posx) (math.floor (- posy (* mvspeed strafe-y)))))
; (set posy (- posy (* mvspeed strafe-y))))
; )
(when (love.keyboard.isDown "r")
(var old-dirx dirx)
(set dirx (- (* dirx (math.cos (- rtspeed))) (* diry (math.sin (- rtspeed)))))
(set diry (+ (* old-dirx (math.sin (- rtspeed))) (* diry (math.cos (- rtspeed)))))
(var old-planex planex)
(set planex (- (* planex (math.cos (- rtspeed))) (* planey (math.sin (- rtspeed)))))
(set planey (+ (* old-planex (math.sin (- rtspeed))) (* planey (math.cos (- rtspeed)))))
)
(when (love.keyboard.isDown "w")
(var old-dirx dirx)
(set dirx (- (* dirx (math.cos rtspeed)) (* diry (math.sin rtspeed))))
(set diry (+ (* old-dirx (math.sin rtspeed)) (* diry (math.cos rtspeed))))
(var old-planex planex)
(set planex (- (* planex (math.cos rtspeed)) (* planey (math.sin rtspeed))))
(set planey (+ (* old-planex (math.sin rtspeed)) (* planey (math.cos rtspeed))))
)
)
:mousemoved (fn mousemoved [x y dx dy]
(var rtspeed (* dx -0.001))
(var yawspeed (* dy -0.5))
(var old-dirx dirx)
(set dirx (- (* dirx (math.cos rtspeed)) (* diry (math.sin rtspeed))))
(set diry (+ (* old-dirx (math.sin rtspeed)) (* diry (math.cos rtspeed))))
(var old-planex planex)
(set planex (- (* planex (math.cos rtspeed)) (* planey (math.sin rtspeed))))
(set planey (+ (* old-planex (math.sin rtspeed)) (* planey (math.cos rtspeed))))
(set offst-y (+ offst-y yawspeed))
)
:keypressed (fn keypressed [k]
(when (= k "x") (love.event.quit))
)
}

29
scripts/update-fennel.sh Executable file
View file

@ -0,0 +1,29 @@
#!/bin/bash
libdir=$1
release=$2
if [ "$#" = 0 ]; then
echo -e "Usage: update-fennel.sh libdir [release]\n\n\
Example: ./update-fennel.sh \$(pwd)/../lib/ 1.2.0"
exit 0;
fi
cd /tmp/
git clone https://github.com/bakpakin/Fennel.git
cd Fennel
if [ $release ]
then
git checkout tags/$release
fi
make
cp fennel fennel.lua $libdir
rm -rf /tmp/Fennel/

35
state.fnl Normal file
View file

@ -0,0 +1,35 @@
; Handles game-play stateful information
(local pi math.pi)
; The Player table holds all the player stuff
; The Inventory table holds the player's inventory
; [x y] coordinates, [d]irection, [f]ield-of-view
; [o]xygen and [p]ower
(var player {:x 3.5 :y 3.5 :d 0 :f (/ pi 3)
:o 100 :p 45})
(var inventory [])
; The Map table holds the map
(var map [])
{:getPlayer (fn getPlayer [] player)
:getX (fn getX [] player.x)
:setX (fn setX [x] (set player.x x))
:getY (fn getY [] player.y)
:setY (fn setY [y] (set player.y y))
:getD (fn getD [] player.d)
:setD (fn setD [d] (set player.d d))
:getX (fn getX [] player.x)
:getO (fn getO [] player.o)
:setO (fn setO [x] (set player.o x))
:modO (fn modO [x]
(if (< (+ player.o x) 0)
(set player.o 0)
(set player.o (+ player.o x))))
:getP (fn getP [] player.p)
:setP (fn setP [x] (set player.p x))
:modP (fn modP [x]
(if (< (+ player.p x) 0)
(set player.p 0)
(set player.p (+ player.p x))))
}

49
test.fnl Normal file
View file

@ -0,0 +1,49 @@
; Cell Stuff
; This will result in a "2x2" cell, of [0 1 1 1], with 0 representing an empty
; space, and 1 representing a wall.
(var (hall-width wall-width) (values 2 2))
; This sets the "map width". This means 10 cells across and down. With each cell
; being 2x2, this results in a 20x20 square map. All maps will be square (for
; now).
(var cell-num 10)
; Explicitly setting cell-size for convenience. All cells are squares
(var cell-size (+ hall-width wall-width))
; Initialize the cell table
(var cells [])
(print (length cells))
(for [c 1 (* cell-num cell-num)]
(var cell [])
(for [i 1 cell-size]
(for [j 1 cell-size]
(var cell-index (+ j (* (- i 1) cell-size)))
(if (and (< i (+ hall-width 1)) (< j (+ hall-width 1)))
(tset cell cell-index 0)
(tset cell cell-index 1))))
(print (length cell))
(table.insert cells cell))
(fn map-row [i] (+ 1 (// (- i 1) cell-num)))
(fn map-index [i] (+ (- (* i cell-size) 1) (* cell-size cell-num (- (map-row i) 1))))
(fn generate_cell_map [cells]
(var cell_map [])
(for [c 1 (length cells)]
(var (mi ci) (values (map-index c) 1))
(for [i 1 cell-size]
(for [j 1 cell-size]
(var new-map-index (+ mi (+ (- i 1) (* (- j 1) (* cell-num cell-size)))))
(var new-cell-index (+ ci (+ (- i 1) (* (- j 1) cell-size))))
(tset cell_map new-map-index (. cells c new-cell-index)))))
cell_map)
(print (length cells))
(print (length (generate_cell_map cells)))
(var output "")
(var ctp (. cells 51))
(for [i 1 (length ctp)]
(set output (.. output " " (. ctp i))))
(print output)

54
wrap.fnl Normal file
View file

@ -0,0 +1,54 @@
(local fennel (require :lib.fennel))
(local repl (require :lib.stdio))
(local canvas (let [(w h) (love.window.getMode)]
(love.graphics.newCanvas w h)))
(var scale 1)
(love.mouse.setGrabbed true)
(love.mouse.setRelativeMode true)
;; set the first mode
(var (mode mode-name) nil)
(fn set-mode [new-mode-name ...]
(set (mode mode-name) (values (require new-mode-name) new-mode-name))
(when mode.activate
(match (pcall mode.activate ...)
(false msg) (print mode-name "activate error" msg))))
(fn love.load [args]
; THIS IS WHERE WE SET THE START MODE
; ###################################
; (set-mode :ray-cast-vectors)
; (set-mode :mode-intro)
(set-mode :raycaster)
; ###################################
(canvas:setFilter "nearest" "nearest")
(when (~= :web (. args 1)) (repl.start)))
(fn safely [f]
(xpcall f #(set-mode :error-mode mode-name $ (fennel.traceback))))
(fn love.draw []
;; the canvas allows you to get sharp pixel-art style scaling; if you
;; don't want that, just skip that and call mode.draw directly.
(love.graphics.setCanvas canvas)
(love.graphics.clear)
(love.graphics.setColor 1 1 1)
(safely mode.draw)
(love.graphics.setCanvas)
(love.graphics.setColor 1 1 1)
(love.graphics.draw canvas 0 0 0 scale scale))
(fn love.update [dt]
(when mode.update
(safely #(mode.update dt set-mode))))
(fn love.mousemoved [x y dx dy istouch]
(safely #(mode.mousemoved x y dx dy istouch)))
(fn love.keypressed [key]
(if (and (love.keyboard.isDown "lctrl" "rctrl" "capslock") (= key "q"))
(love.event.quit)
;; add what each keypress should do in each mode
(safely #(mode.keypressed key set-mode))))