From ce2737c35395005e6bdf2e6305a40a17d534f617 Mon Sep 17 00:00:00 2001 From: g012 Date: Tue, 10 Oct 2017 02:04:25 +0200 Subject: [PATCH] Fixed erroneous function name in lua.h. Added lpeg's re.lua lib. Added dkjson.lua. Added parser for TIATracker .ttt files. --- CMakeLists.txt | 12 +- README.md | 10 +- dkjson.lua | 713 ++++++++++++ embed.c | 50 +- l65cfg.lua | 4 +- lua.h | 32 +- main.c | 10 +- re.lua | 249 +++++ samples/Ishtar.ttt | 2561 ++++++++++++++++++++++++++++++++++++++++++++ vcs.l65 | 137 +++ 10 files changed, 3744 insertions(+), 34 deletions(-) create mode 100644 dkjson.lua create mode 100644 re.lua create mode 100644 samples/Ishtar.ttt diff --git a/CMakeLists.txt b/CMakeLists.txt index 4692fd0..21f1122 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ set(L65_SOURCE_DIR ${PROJECT_SOURCE_DIR}) set(L65_BINARY_DIR ${PROJECT_BINARY_DIR}) set(L65_VERSION_MAJOR "1") -set(L65_VERSION_MINOR "0") +set(L65_VERSION_MINOR "1") set(L65_VERSION_REVISION "0") set(L65_VERSION "${L65_VERSION_MAJOR}.${L65_VERSION_MINOR}.${L65_VERSION_REVISION}") configure_file("${L65_SOURCE_DIR}/l65cfg.lua.in" "${L65_BINARY_DIR}/l65cfg.lua") @@ -77,18 +77,14 @@ set(L65_HEADERS ${L65_BINARY_DIR}/scripts.h ${L65_SOURCE_DIR}/stb_image.h ) -set(L65_RESOURCES - ${L65_SOURCE_DIR}/6502.lua - ${L65_SOURCE_DIR}/l65.lua - ${L65_BINARY_DIR}/l65cfg.lua - ${L65_SOURCE_DIR}/vcs.l65 - ) file(GLOB L65_FILES ${L65_SOURCE_DIR}/*.l65) set(L65_SCRIPTS ${L65_SOURCE_DIR}/6502.lua + ${L65_SOURCE_DIR}/dkjson.lua ${L65_SOURCE_DIR}/l65.lua ${L65_BINARY_DIR}/l65cfg.lua + ${L65_SOURCE_DIR}/re.lua ${L65_FILES} ) @@ -100,7 +96,7 @@ add_custom_command( DEPENDS embed ${L65_SCRIPTS} ) add_custom_target(prereq DEPENDS ${L65_BINARY_DIR}/scripts.h) -add_executable(${PROJECT_NAME} ${L65_SOURCES} ${L65_HEADERS} ${L65_RESOURCES}) +add_executable(${PROJECT_NAME} ${L65_SOURCES} ${L65_HEADERS} ${L65_FILES}) add_dependencies(${PROJECT_NAME} prereq) set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99) target_include_directories(${PROJECT_NAME} PRIVATE "${L65_SOURCE_DIR}" "${L65_BINARY_DIR}") diff --git a/README.md b/README.md index 6db2fe7..f9d20bd 100644 --- a/README.md +++ b/README.md @@ -617,19 +617,19 @@ Note that the syntax file includes some highlighting for features only activated * [k65](http://devkk.net/wiki/index.php?title=K65) style syntax * helpers to inter-operate with cc/ca65 and dasm + * import TIATracker \*.ttt files directly ## Credits Developed by g012, using: * [Lua 5.3.4](https://www.lua.org) * [LuaMinify](https://github.com/stravant/LuaMinify) - * Lua syntax file shipping with [vim](http://www.vim.org) - * Lua indent file from [vim-lua](https://github.com/tbastos/vim-lua) - * [stb_image](https://github.com/nothings/stb) - -Not using, but integrated for end-user convenience: * [LuaFileSystem](https://keplerproject.github.io/luafilesystem) * [LPeg](http://www.inf.puc-rio.br/~roberto/lpeg) + * [stb_image](https://github.com/nothings/stb) + * [dkjson](http://dkolf.de/src/dkjson-lua.fsl) + * Lua syntax file shipping with [vim](http://www.vim.org) + * Lua indent file from [vim-lua](https://github.com/tbastos/vim-lua) Projects which inspired l65: * [nimble65](https://bitbucket.org/kylearan/nimble65) diff --git a/dkjson.lua b/dkjson.lua new file mode 100644 index 0000000..89aa2e1 --- /dev/null +++ b/dkjson.lua @@ -0,0 +1,713 @@ +-- Module options: +local always_try_using_lpeg = true +local register_global_module_table = false +local global_module_name = 'json' + +--[==[ + +David Kolf's JSON module for Lua 5.1/5.2 + +Version 2.5 + + +For the documentation see the corresponding readme.txt or visit +. + +You can contact the author by sending an e-mail to 'david' at the +domain 'dkolf.de'. + + +Copyright (C) 2010-2013 David Heiko Kolf + +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. + +--]==] + +-- global dependencies: +local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset = + pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset +local error, require, pcall, select = error, require, pcall, select +local floor, huge = math.floor, math.huge +local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat = + string.rep, string.gsub, string.sub, string.byte, string.char, + string.find, string.len, string.format +local strmatch = string.match +local concat = table.concat + +local json = { version = "dkjson 2.5" } + +if register_global_module_table then + _G[global_module_name] = json +end + +local _ENV = nil -- blocking globals in Lua 5.2 + +pcall (function() + -- Enable access to blocked metatables. + -- Don't worry, this module doesn't change anything in them. + local debmeta = require "debug".getmetatable + if debmeta then getmetatable = debmeta end +end) + +json.null = setmetatable ({}, { + __tojson = function () return "null" end +}) + +local function isarray (tbl) + local max, n, arraylen = 0, 0, 0 + for k,v in pairs (tbl) do + if k == 'n' and type(v) == 'number' then + arraylen = v + if v > max then + max = v + end + else + if type(k) ~= 'number' or k < 1 or floor(k) ~= k then + return false + end + if k > max then + max = k + end + n = n + 1 + end + end + if max > 10 and max > arraylen and max > n * 2 then + return false -- don't create an array with too many holes + end + return true, max +end + +local escapecodes = { + ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", + ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" +} + +local function escapeutf8 (uchar) + local value = escapecodes[uchar] + if value then + return value + end + local a, b, c, d = strbyte (uchar, 1, 4) + a, b, c, d = a or 0, b or 0, c or 0, d or 0 + if a <= 0x7f then + value = a + elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then + value = (a - 0xc0) * 0x40 + b - 0x80 + elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then + value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80 + elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then + value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80 + else + return "" + end + if value <= 0xffff then + return strformat ("\\u%.4x", value) + elseif value <= 0x10ffff then + -- encode as UTF-16 surrogate pair + value = value - 0x10000 + local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400) + return strformat ("\\u%.4x\\u%.4x", highsur, lowsur) + else + return "" + end +end + +local function fsub (str, pattern, repl) + -- gsub always builds a new string in a buffer, even when no match + -- exists. First using find should be more efficient when most strings + -- don't contain the pattern. + if strfind (str, pattern) then + return gsub (str, pattern, repl) + else + return str + end +end + +local function quotestring (value) + -- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js + value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8) + if strfind (value, "[\194\216\220\225\226\239]") then + value = fsub (value, "\194[\128-\159\173]", escapeutf8) + value = fsub (value, "\216[\128-\132]", escapeutf8) + value = fsub (value, "\220\143", escapeutf8) + value = fsub (value, "\225\158[\180\181]", escapeutf8) + value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8) + value = fsub (value, "\226\129[\160-\175]", escapeutf8) + value = fsub (value, "\239\187\191", escapeutf8) + value = fsub (value, "\239\191[\176-\191]", escapeutf8) + end + return "\"" .. value .. "\"" +end +json.quotestring = quotestring + +local function replace(str, o, n) + local i, j = strfind (str, o, 1, true) + if i then + return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1) + else + return str + end +end + +-- locale independent num2str and str2num functions +local decpoint, numfilter + +local function updatedecpoint () + decpoint = strmatch(tostring(0.5), "([^05+])") + -- build a filter that can be used to remove group separators + numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+" +end + +updatedecpoint() + +local function num2str (num) + return replace(fsub(tostring(num), numfilter, ""), decpoint, ".") +end + +local function str2num (str) + local num = tonumber(replace(str, ".", decpoint)) + if not num then + updatedecpoint() + num = tonumber(replace(str, ".", decpoint)) + end + return num +end + +local function addnewline2 (level, buffer, buflen) + buffer[buflen+1] = "\n" + buffer[buflen+2] = strrep (" ", level) + buflen = buflen + 2 + return buflen +end + +function json.addnewline (state) + if state.indent then + state.bufferlen = addnewline2 (state.level or 0, + state.buffer, state.bufferlen or #(state.buffer)) + end +end + +local encode2 -- forward declaration + +local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state) + local kt = type (key) + if kt ~= 'string' and kt ~= 'number' then + return nil, "type '" .. kt .. "' is not supported as a key by JSON." + end + if prev then + buflen = buflen + 1 + buffer[buflen] = "," + end + if indent then + buflen = addnewline2 (level, buffer, buflen) + end + buffer[buflen+1] = quotestring (key) + buffer[buflen+2] = ":" + return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state) +end + +local function appendcustom(res, buffer, state) + local buflen = state.bufferlen + if type (res) == 'string' then + buflen = buflen + 1 + buffer[buflen] = res + end + return buflen +end + +local function exception(reason, value, state, buffer, buflen, defaultmessage) + defaultmessage = defaultmessage or reason + local handler = state.exception + if not handler then + return nil, defaultmessage + else + state.bufferlen = buflen + local ret, msg = handler (reason, value, state, defaultmessage) + if not ret then return nil, msg or defaultmessage end + return appendcustom(ret, buffer, state) + end +end + +function json.encodeexception(reason, value, state, defaultmessage) + return quotestring("<" .. defaultmessage .. ">") +end + +encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state) + local valtype = type (value) + local valmeta = getmetatable (value) + valmeta = type (valmeta) == 'table' and valmeta -- only tables + local valtojson = valmeta and valmeta.__tojson + if valtojson then + if tables[value] then + return exception('reference cycle', value, state, buffer, buflen) + end + tables[value] = true + state.bufferlen = buflen + local ret, msg = valtojson (value, state) + if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end + tables[value] = nil + buflen = appendcustom(ret, buffer, state) + elseif value == nil then + buflen = buflen + 1 + buffer[buflen] = "null" + elseif valtype == 'number' then + local s + if value ~= value or value >= huge or -value >= huge then + -- This is the behaviour of the original JSON implementation. + s = "null" + else + s = num2str (value) + end + buflen = buflen + 1 + buffer[buflen] = s + elseif valtype == 'boolean' then + buflen = buflen + 1 + buffer[buflen] = value and "true" or "false" + elseif valtype == 'string' then + buflen = buflen + 1 + buffer[buflen] = quotestring (value) + elseif valtype == 'table' then + if tables[value] then + return exception('reference cycle', value, state, buffer, buflen) + end + tables[value] = true + level = level + 1 + local isa, n = isarray (value) + if n == 0 and valmeta and valmeta.__jsontype == 'object' then + isa = false + end + local msg + if isa then -- JSON array + buflen = buflen + 1 + buffer[buflen] = "[" + for i = 1, n do + buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + if i < n then + buflen = buflen + 1 + buffer[buflen] = "," + end + end + buflen = buflen + 1 + buffer[buflen] = "]" + else -- JSON object + local prev = false + buflen = buflen + 1 + buffer[buflen] = "{" + local order = valmeta and valmeta.__jsonorder or globalorder + if order then + local used = {} + n = #order + for i = 1, n do + local k = order[i] + local v = value[k] + if v then + used[k] = true + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + prev = true -- add a seperator before the next element + end + end + for k,v in pairs (value) do + if not used[k] then + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + prev = true -- add a seperator before the next element + end + end + else -- unordered + for k,v in pairs (value) do + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + prev = true -- add a seperator before the next element + end + end + if indent then + buflen = addnewline2 (level - 1, buffer, buflen) + end + buflen = buflen + 1 + buffer[buflen] = "}" + end + tables[value] = nil + else + return exception ('unsupported type', value, state, buffer, buflen, + "type '" .. valtype .. "' is not supported by JSON.") + end + return buflen +end + +function json.encode (value, state) + state = state or {} + local oldbuffer = state.buffer + local buffer = oldbuffer or {} + state.buffer = buffer + updatedecpoint() + local ret, msg = encode2 (value, state.indent, state.level or 0, + buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state) + if not ret then + error (msg, 2) + elseif oldbuffer == buffer then + state.bufferlen = ret + return true + else + state.bufferlen = nil + state.buffer = nil + return concat (buffer) + end +end + +local function loc (str, where) + local line, pos, linepos = 1, 1, 0 + while true do + pos = strfind (str, "\n", pos, true) + if pos and pos < where then + line = line + 1 + linepos = pos + pos = pos + 1 + else + break + end + end + return "line " .. line .. ", column " .. (where - linepos) +end + +local function unterminated (str, what, where) + return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where) +end + +local function scanwhite (str, pos) + while true do + pos = strfind (str, "%S", pos) + if not pos then return nil end + local sub2 = strsub (str, pos, pos + 1) + if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then + -- UTF-8 Byte Order Mark + pos = pos + 3 + elseif sub2 == "//" then + pos = strfind (str, "[\n\r]", pos + 2) + if not pos then return nil end + elseif sub2 == "/*" then + pos = strfind (str, "*/", pos + 2) + if not pos then return nil end + pos = pos + 2 + else + return pos + end + end +end + +local escapechars = { + ["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f", + ["n"] = "\n", ["r"] = "\r", ["t"] = "\t" +} + +local function unichar (value) + if value < 0 then + return nil + elseif value <= 0x007f then + return strchar (value) + elseif value <= 0x07ff then + return strchar (0xc0 + floor(value/0x40), + 0x80 + (floor(value) % 0x40)) + elseif value <= 0xffff then + return strchar (0xe0 + floor(value/0x1000), + 0x80 + (floor(value/0x40) % 0x40), + 0x80 + (floor(value) % 0x40)) + elseif value <= 0x10ffff then + return strchar (0xf0 + floor(value/0x40000), + 0x80 + (floor(value/0x1000) % 0x40), + 0x80 + (floor(value/0x40) % 0x40), + 0x80 + (floor(value) % 0x40)) + else + return nil + end +end + +local function scanstring (str, pos) + local lastpos = pos + 1 + local buffer, n = {}, 0 + while true do + local nextpos = strfind (str, "[\"\\]", lastpos) + if not nextpos then + return unterminated (str, "string", pos) + end + if nextpos > lastpos then + n = n + 1 + buffer[n] = strsub (str, lastpos, nextpos - 1) + end + if strsub (str, nextpos, nextpos) == "\"" then + lastpos = nextpos + 1 + break + else + local escchar = strsub (str, nextpos + 1, nextpos + 1) + local value + if escchar == "u" then + value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16) + if value then + local value2 + if 0xD800 <= value and value <= 0xDBff then + -- we have the high surrogate of UTF-16. Check if there is a + -- low surrogate escaped nearby to combine them. + if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then + value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16) + if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then + value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000 + else + value2 = nil -- in case it was out of range for a low surrogate + end + end + end + value = value and unichar (value) + if value then + if value2 then + lastpos = nextpos + 12 + else + lastpos = nextpos + 6 + end + end + end + end + if not value then + value = escapechars[escchar] or escchar + lastpos = nextpos + 2 + end + n = n + 1 + buffer[n] = value + end + end + if n == 1 then + return buffer[1], lastpos + elseif n > 1 then + return concat (buffer), lastpos + else + return "", lastpos + end +end + +local scanvalue -- forward declaration + +local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta) + local len = strlen (str) + local tbl, n = {}, 0 + local pos = startpos + 1 + if what == 'object' then + setmetatable (tbl, objectmeta) + else + setmetatable (tbl, arraymeta) + end + while true do + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + local char = strsub (str, pos, pos) + if char == closechar then + return tbl, pos + 1 + end + local val1, err + val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) + if err then return nil, pos, err end + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + char = strsub (str, pos, pos) + if char == ":" then + if val1 == nil then + return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")" + end + pos = scanwhite (str, pos + 1) + if not pos then return unterminated (str, what, startpos) end + local val2 + val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) + if err then return nil, pos, err end + tbl[val1] = val2 + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + char = strsub (str, pos, pos) + else + n = n + 1 + tbl[n] = val1 + end + if char == "," then + pos = pos + 1 + end + end +end + +scanvalue = function (str, pos, nullval, objectmeta, arraymeta) + pos = pos or 1 + pos = scanwhite (str, pos) + if not pos then + return nil, strlen (str) + 1, "no valid JSON value (reached the end)" + end + local char = strsub (str, pos, pos) + if char == "{" then + return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta) + elseif char == "[" then + return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta) + elseif char == "\"" then + return scanstring (str, pos) + else + local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos) + if pstart then + local number = str2num (strsub (str, pstart, pend)) + if number then + return number, pend + 1 + end + end + pstart, pend = strfind (str, "^%a%w*", pos) + if pstart then + local name = strsub (str, pstart, pend) + if name == "true" then + return true, pend + 1 + elseif name == "false" then + return false, pend + 1 + elseif name == "null" then + return nullval, pend + 1 + end + end + return nil, pos, "no valid JSON value at " .. loc (str, pos) + end +end + +local function optionalmetatables(...) + if select("#", ...) > 0 then + return ... + else + return {__jsontype = 'object'}, {__jsontype = 'array'} + end +end + +function json.decode (str, pos, nullval, ...) + local objectmeta, arraymeta = optionalmetatables(...) + return scanvalue (str, pos, nullval, objectmeta, arraymeta) +end + +function json.use_lpeg () + local g = require ("lpeg") + + if g.version() == "0.11" then + error "due to a bug in LPeg 0.11, it cannot be used for JSON matching" + end + + local pegmatch = g.match + local P, S, R = g.P, g.S, g.R + + local function ErrorCall (str, pos, msg, state) + if not state.msg then + state.msg = msg .. " at " .. loc (str, pos) + state.pos = pos + end + return false + end + + local function Err (msg) + return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall) + end + + local SingleLineComment = P"//" * (1 - S"\n\r")^0 + local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/" + local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0 + + local PlainChar = 1 - S"\"\\\n\r" + local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars + local HexDigit = R("09", "af", "AF") + local function UTF16Surrogate (match, pos, high, low) + high, low = tonumber (high, 16), tonumber (low, 16) + if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then + return true, unichar ((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000) + else + return false + end + end + local function UTF16BMP (hex) + return unichar (tonumber (hex, 16)) + end + local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit)) + local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP + local Char = UnicodeEscape + EscapeSequence + PlainChar + local String = P"\"" * g.Cs (Char ^ 0) * (P"\"" + Err "unterminated string") + local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0)) + local Fractal = P"." * R"09"^0 + local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1 + local Number = (Integer * Fractal^(-1) * Exponent^(-1))/str2num + local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1) + local SimpleValue = Number + String + Constant + local ArrayContent, ObjectContent + + -- The functions parsearray and parseobject parse only a single value/pair + -- at a time and store them directly to avoid hitting the LPeg limits. + local function parsearray (str, pos, nullval, state) + local obj, cont + local npos + local t, nt = {}, 0 + repeat + obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state) + if not npos then break end + pos = npos + nt = nt + 1 + t[nt] = obj + until cont == 'last' + return pos, setmetatable (t, state.arraymeta) + end + + local function parseobject (str, pos, nullval, state) + local obj, key, cont + local npos + local t = {} + repeat + key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state) + if not npos then break end + pos = npos + t[key] = obj + until cont == 'last' + return pos, setmetatable (t, state.objectmeta) + end + + local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) * Space * (P"]" + Err "']' expected") + local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) * Space * (P"}" + Err "'}' expected") + local Value = Space * (Array + Object + SimpleValue) + local ExpectedValue = Value + Space * Err "value expected" + ArrayContent = Value * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() + local Pair = g.Cg (Space * String * Space * (P":" + Err "colon expected") * ExpectedValue) + ObjectContent = Pair * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() + local DecodeValue = ExpectedValue * g.Cp () + + function json.decode (str, pos, nullval, ...) + local state = {} + state.objectmeta, state.arraymeta = optionalmetatables(...) + local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state) + if state.msg then + return nil, state.pos, state.msg + else + return obj, retpos + end + end + + -- use this function only once: + json.use_lpeg = function () return json end + + json.using_lpeg = true + + return json -- so you can get the module using json = require "dkjson".use_lpeg() +end + +if always_try_using_lpeg then + pcall (json.use_lpeg) +end + +return json diff --git a/embed.c b/embed.c index 27ec434..e8f7d7c 100644 --- a/embed.c +++ b/embed.c @@ -80,6 +80,44 @@ static int writer(lua_State* L, const void* p, size_t size, void* f) return 0; } +// use custom version so that filename is not used for debug info +LUALIB_API int luaL_loadfilex2 (lua_State *L, const char *filename, + const char *mode, const char *chunkname) { + luai_LoadF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + if (filename == NULL) { + lua_pushliteral(L, "=stdin"); + lf.f = stdin; + } + else { + lua_pushfstring(L, "@%s", filename); + lf.f = fopen(filename, "r"); + if (lf.f == NULL) return luai_errfile(L, "open", fnameindex); + } + if (luai_skipcomment(&lf, &c)) /* read initial portion */ + lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ + if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ + lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ + if (lf.f == NULL) return luai_errfile(L, "reopen", fnameindex); + luai_skipcomment(&lf, &c); /* re-read initial portion */ + } + if (c != EOF) + lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ + lua_pop(L, 1); + lua_pushstring(L, chunkname); + status = lua_load(L, luai_getF, &lf, lua_tostring(L, -1), mode); + readstatus = ferror(lf.f); + if (filename) fclose(lf.f); /* close file (even in case of errors) */ + if (readstatus) { + lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ + return luai_errfile(L, "read", fnameindex); + } + lua_remove(L, fnameindex); + return status; +} + static int pmain(lua_State* L) { int argc = (int)lua_tointeger(L, 1); @@ -102,7 +140,17 @@ static int pmain(lua_State* L) compile = fnl > 4 && !strcmp(".lua", filename + fnl - 4); if (compile) { - if (luaL_loadfile(L, filename) != LUA_OK) fatal(lua_tostring(L, -1)); + // do no use luaL_loadfile so that the filename is not used for error printing + char *p = strdup(filename), *n = p; + for (p = p + strlen(p); p != n && *p != '/' && *p != '\\'; --p) ; + if (p != n) ++p; + char *chunkname = malloc(strlen(p) + 16); + strcpy(chunkname, "=("); + strcat(chunkname, p); + strcat(chunkname, ")"); + free(n); + if (luaL_loadfilex2(L, filename, 0, chunkname) != LUA_OK) fatal(lua_tostring(L, -1)); + free(chunkname); fprintf(f, "static const char %s[] = {", name); w_o = 0; lua_dump(L, writer, f, 0); diff --git a/l65cfg.lua b/l65cfg.lua index cbf9973..0b12650 100644 --- a/l65cfg.lua +++ b/l65cfg.lua @@ -1,8 +1,8 @@ local M = {} -M.major = 1 +M.major = 0 M.minor = 0 M.revision = 0 -M.version = "1.0.0" +M.version = "0.0.0" return M diff --git a/lua.h b/lua.h index 3393b27..ec1dd0f 100644 --- a/lua.h +++ b/lua.h @@ -6353,7 +6353,7 @@ static int luaB_rawset (lua_State *L) { static int luaB_collectgarbage (lua_State *L) { static const char *const opts[] = {"stop", "restart", "collect", - "count", "step", "luai_setpause", "setstepmul", + "count", "step", "setpause", "setstepmul", "isrunning", NULL}; static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, @@ -6643,7 +6643,7 @@ static const luaL_Reg luai_base_funcs[] = { #if defined(LUA_COMPAT_LOADSTRING) {"loadstring", luaB_load}, #endif - {"luai_next", luaB_next}, + {"next", luaB_next}, {"pairs", luaB_pairs}, {"pcall", luaB_pcall}, {"print", luaB_print}, @@ -6653,7 +6653,7 @@ static const luaL_Reg luai_base_funcs[] = { {"rawset", luaB_rawset}, {"select", luaB_select}, {"setmetatable", luaB_setmetatable}, - {"luai_tonumber", luaB_tonumber}, + {"tonumber", luaB_tonumber}, {"tostring", luaB_tostring}, {"type", luaB_type}, {"xpcall", luaB_xpcall}, @@ -6831,7 +6831,7 @@ static int lua_b_rrot (lua_State *L) { static int lua_fieldargs (lua_State *L, int farg, int *width) { lua_Integer f = luaL_checkinteger(L, farg); lua_Integer w = luaL_optinteger(L, farg + 1, 1); - luaL_argcheck(L, 0 <= f, farg, "luai_field cannot be negative"); + luaL_argcheck(L, 0 <= f, farg, "field cannot be negative"); luaL_argcheck(L, 0 < w, farg + 1, "width must be positive"); if (f + w > LUA_NBITS) luaL_error(L, "trying to access non-existent bits"); @@ -8066,7 +8066,7 @@ void luaK_setlist (luai_FuncState *fs, int base, int nelems, int tostore) { luai_codeextraarg(fs, c); } else - luaX_syntaxerror(fs->ls, "luai_constructor too long"); + luaX_syntaxerror(fs->ls, "constructor too long"); fs->luai_freereg = base + 1; /* free registers with list values */ } @@ -8204,7 +8204,7 @@ static int luaB_corunning (lua_State *L) { static const luaL_Reg luai_co_funcs[] = { {"create", luaB_cocreate}, - {"luai_resume", luaB_coresume}, + {"resume", luaB_coresume}, {"running", luaB_corunning}, {"status", luaB_costatus}, {"wrap", luaB_cowrap}, @@ -8416,7 +8416,7 @@ static int luai_db_getinfo (lua_State *L) { luai_settabss(L, "what", ar.what); } if (strchr(options, 'l')) - luai_settabsi(L, "luai_currentline", ar.luai_currentline); + luai_settabsi(L, "currentline", ar.luai_currentline); if (strchr(options, 'u')) { luai_settabsi(L, "nups", ar.nups); luai_settabsi(L, "nparams", ar.nparams); @@ -8676,7 +8676,7 @@ static int luai_db_traceback (lua_State *L) { static const luaL_Reg luai_dblib[] = { {"debug", luai_db_debug}, - {"luai_getuservalue", luai_db_getuservalue}, + {"getuservalue", luai_db_getuservalue}, {"gethook", luai_db_gethook}, {"getinfo", luai_db_getinfo}, {"getlocal", luai_db_getlocal}, @@ -8685,7 +8685,7 @@ static const luaL_Reg luai_dblib[] = { {"getupvalue", luai_db_getupvalue}, {"upvaluejoin", luai_db_upvaluejoin}, {"upvalueid", luai_db_upvalueid}, - {"luai_setuservalue", luai_db_setuservalue}, + {"setuservalue", luai_db_setuservalue}, {"sethook", luai_db_sethook}, {"setlocal", luai_db_setlocal}, {"setmetatable", luai_db_setmetatable}, @@ -9125,7 +9125,7 @@ static const char *luai_getobjname (luai_Proto *p, int lastpc, int reg, ? luaF_getlocalname(p, t + 1, pc) : luai_upvalname(p, t); luai_kname(p, pc, k, name); - return (vn && strcmp(vn, LUA_ENV) == 0) ? "global" : "luai_field"; + return (vn && strcmp(vn, LUA_ENV) == 0) ? "global" : "field"; } case luai_OP_GETUPVAL: { *name = luai_upvalname(p, luai_GETARG_B(i)); @@ -9894,7 +9894,7 @@ static void luai_unroll (lua_State *L, void *ud) { /* -** Try to find a suspended protected call (a "luai_recover point") for the +** Try to find a suspended protected call (a "recover point") for the ** given thread. */ static luai_CallInfo *luai_findpcall (lua_State *L) { @@ -13264,7 +13264,7 @@ static const luaL_Reg luai_mathlib[] = { {"cos", luai_math_cos}, {"deg", luai_math_deg}, {"exp", luai_math_exp}, - {"luai_tointeger", luai_math_toint}, + {"tointeger", luai_math_toint}, {"floor", luai_math_floor}, {"fmod", luai_math_fmod}, {"ult", luai_math_ult}, @@ -14979,14 +14979,14 @@ static int luai_getfield (lua_State *L, const char *key, int d, int delta) { lua_Integer res = lua_tointegerx(L, -1, &isnum); if (!isnum) { /* luai_field is not an integer? */ if (t != LUA_TNIL) /* some other value? */ - return luaL_error(L, "luai_field '%s' is not an integer", key); + return luaL_error(L, "field '%s' is not an integer", key); else if (d < 0) /* absent luai_field; no default? */ - return luaL_error(L, "luai_field '%s' missing in date table", key); + return luaL_error(L, "field '%s' missing in date table", key); res = d; } else { if (!(-LUAI_L_MAXDATEFIELD <= res && res <= LUAI_L_MAXDATEFIELD)) - return luaL_error(L, "luai_field '%s' is out-of-bound", key); + return luaL_error(L, "field '%s' is out-of-bound", key); res -= delta; } lua_pop(L, 1); @@ -19958,7 +19958,7 @@ void luaT_init (lua_State *L) { /* -** function to be used with macro "luai_fasttm": optimized for absence of +** function to be used with macro "fasttm": optimized for absence of ** tag methods */ const luai_TValue *luaT_gettm (luai_Table *events, luai_TMS event, luai_TString *ename) { diff --git a/main.c b/main.c index 228876a..43b270e 100644 --- a/main.c +++ b/main.c @@ -139,10 +139,16 @@ static int luaopen_l65(lua_State *L) return 1; } +#define SRC_LUA(name) { #name, 0, script_ ## name ## _lua, sizeof(script_ ## name ## _lua) } +#define SRC_L65(name) { #name, 1, script_ ## name ## _l65, sizeof(script_ ## name ## _l65) } static struct script { const char *name; int t; const char *data; size_t sz; } embedded[] = { - { "l65cfg", 0, script_l65cfg_lua, sizeof(script_l65cfg_lua) }, - { "vcs", 1, script_vcs_l65, sizeof(script_vcs_l65) }, + SRC_LUA(dkjson), + SRC_LUA(l65cfg), + SRC_LUA(re), + SRC_L65(vcs), }; +#undef SRC_LUA +#undef SRC_L65 static int getembedded(lua_State *L) { diff --git a/re.lua b/re.lua new file mode 100644 index 0000000..8df8b61 --- /dev/null +++ b/re.lua @@ -0,0 +1,249 @@ +-- $Id: re.lua,v 1.44 2013/03/26 20:11:40 roberto Exp $ + +-- imported functions and modules +local tonumber, type, print, error = tonumber, type, print, error +local setmetatable = setmetatable +local m = require"lpeg" + +-- 'm' will be used to parse expressions, and 'mm' will be used to +-- create expressions; that is, 're' runs on 'm', creating patterns +-- on 'mm' +local mm = m + +-- pattern's metatable +local mt = getmetatable(mm.P(0)) + +local any = m.P(1) + +-- Pre-defined names +local Predef = { nl = m.P"\n" } + + +local mem +local fmem +local gmem + + +local function updatelocale () + mm.locale(Predef) + Predef.a = Predef.alpha + Predef.c = Predef.cntrl + Predef.d = Predef.digit + Predef.g = Predef.graph + Predef.l = Predef.lower + Predef.p = Predef.punct + Predef.s = Predef.space + Predef.u = Predef.upper + Predef.w = Predef.alnum + Predef.x = Predef.xdigit + Predef.A = any - Predef.a + Predef.C = any - Predef.c + Predef.D = any - Predef.d + Predef.G = any - Predef.g + Predef.L = any - Predef.l + Predef.P = any - Predef.p + Predef.S = any - Predef.s + Predef.U = any - Predef.u + Predef.W = any - Predef.w + Predef.X = any - Predef.x + mem = {} -- restart memoization + fmem = {} + gmem = {} + local mt = {__mode = "v"} + setmetatable(mem, mt) + setmetatable(fmem, mt) + setmetatable(gmem, mt) +end + + +updatelocale() + + + +local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end) + + +local function getdef (id, defs) + local c = defs and defs[id] + if not c then error("undefined name: " .. id) end + return c +end + + +local function patt_error (s, i) + local msg = (#s < i + 20) and s:sub(i) + or s:sub(i,i+20) .. "..." + msg = ("pattern error near '%s'"):format(msg) + error(msg, 2) +end + +local function mult (p, n) + local np = mm.P(true) + while n >= 1 do + if n%2 >= 1 then np = np * p end + p = p * p + n = n/2 + end + return np +end + +local function equalcap (s, i, c) + if type(c) ~= "string" then return nil end + local e = #c + i + if s:sub(i, e - 1) == c then return e else return nil end +end + + +local S = (Predef.space + "--" * (any - Predef.nl)^0)^0 + +local name = m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0 + +local arrow = S * "<-" + +local seq_follow = m.P"/" + ")" + "}" + ":}" + "~}" + "|}" + (name * arrow) + -1 + +name = m.C(name) + + +-- a defined name only have meaning in a given environment +local Def = name * m.Carg(1) + +local num = m.C(m.R"09"^1) * S / tonumber + +local String = "'" * m.C((any - "'")^0) * "'" + + '"' * m.C((any - '"')^0) * '"' + + +local defined = "%" * Def / function (c,Defs) + local cat = Defs and Defs[c] or Predef[c] + if not cat then error ("name '" .. c .. "' undefined") end + return cat +end + +local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R + +local item = defined + Range + m.C(any) + +local Class = + "[" + * (m.C(m.P"^"^-1)) -- optional complement symbol + * m.Cf(item * (item - "]")^0, mt.__add) / + function (c, p) return c == "^" and any - p or p end + * "]" + +local function adddef (t, k, exp) + if t[k] then + error("'"..k.."' already defined as a rule") + else + t[k] = exp + end + return t +end + +local function firstdef (n, r) return adddef({n}, n, r) end + + +local function NT (n, b) + if not b then + error("rule '"..n.."' used outside a grammar") + else return mm.V(n) + end +end + + +local exp = m.P{ "Exp", + Exp = S * ( m.V"Grammar" + + m.Cf(m.V"Seq" * ("/" * S * m.V"Seq")^0, mt.__add) ); + Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix"^0 , mt.__mul) + * (#seq_follow + patt_error); + Prefix = "&" * S * m.V"Prefix" / mt.__len + + "!" * S * m.V"Prefix" / mt.__unm + + m.V"Suffix"; + Suffix = m.Cf(m.V"Primary" * S * + ( ( m.P"+" * m.Cc(1, mt.__pow) + + m.P"*" * m.Cc(0, mt.__pow) + + m.P"?" * m.Cc(-1, mt.__pow) + + "^" * ( m.Cg(num * m.Cc(mult)) + + m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow)) + ) + + "->" * S * ( m.Cg((String + num) * m.Cc(mt.__div)) + + m.P"{}" * m.Cc(nil, m.Ct) + + m.Cg(Def / getdef * m.Cc(mt.__div)) + ) + + "=>" * S * m.Cg(Def / getdef * m.Cc(m.Cmt)) + ) * S + )^0, function (a,b,f) return f(a,b) end ); + Primary = "(" * m.V"Exp" * ")" + + String / mm.P + + Class + + defined + + "{:" * (name * ":" + m.Cc(nil)) * m.V"Exp" * ":}" / + function (n, p) return mm.Cg(p, n) end + + "=" * name / function (n) return mm.Cmt(mm.Cb(n), equalcap) end + + m.P"{}" / mm.Cp + + "{~" * m.V"Exp" * "~}" / mm.Cs + + "{|" * m.V"Exp" * "|}" / mm.Ct + + "{" * m.V"Exp" * "}" / mm.C + + m.P"." * m.Cc(any) + + (name * -arrow + "<" * name * ">") * m.Cb("G") / NT; + Definition = name * arrow * m.V"Exp"; + Grammar = m.Cg(m.Cc(true), "G") * + m.Cf(m.V"Definition" / firstdef * m.Cg(m.V"Definition")^0, + adddef) / mm.P +} + +local pattern = S * m.Cg(m.Cc(false), "G") * exp / mm.P * (-any + patt_error) + + +local function compile (p, defs) + if mm.type(p) == "pattern" then return p end -- already compiled + local cp = pattern:match(p, 1, defs) + if not cp then error("incorrect pattern", 3) end + return cp +end + +local function match (s, p, i) + local cp = mem[p] + if not cp then + cp = compile(p) + mem[p] = cp + end + return cp:match(s, i or 1) +end + +local function find (s, p, i) + local cp = fmem[p] + if not cp then + cp = compile(p) / 0 + cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) } + fmem[p] = cp + end + local i, e = cp:match(s, i or 1) + if i then return i, e - 1 + else return i + end +end + +local function gsub (s, p, rep) + local g = gmem[p] or {} -- ensure gmem[p] is not collected while here + gmem[p] = g + local cp = g[rep] + if not cp then + cp = compile(p) + cp = mm.Cs((cp / rep + 1)^0) + g[rep] = cp + end + return cp:match(s) +end + + +-- exported names +local re = { + compile = compile, + match = match, + find = find, + gsub = gsub, + updatelocale = updatelocale, +} + +return re diff --git a/samples/Ishtar.ttt b/samples/Ishtar.ttt new file mode 100644 index 0000000..bdfc8de --- /dev/null +++ b/samples/Ishtar.ttt @@ -0,0 +1,2561 @@ +{ + "channels": [ + { + "sequence": [ + { + "gototarget": -1, + "patternindex": 0 + }, + { + "gototarget": -1, + "patternindex": 0 + }, + { + "gototarget": -1, + "patternindex": 2 + }, + { + "gototarget": -1, + "patternindex": 3 + }, + { + "gototarget": -1, + "patternindex": 0 + }, + { + "gototarget": -1, + "patternindex": 0 + }, + { + "gototarget": -1, + "patternindex": 2 + }, + { + "gototarget": -1, + "patternindex": 3 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 6 + }, + { + "gototarget": -1, + "patternindex": 7 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 6 + }, + { + "gototarget": -1, + "patternindex": 7 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 6 + }, + { + "gototarget": -1, + "patternindex": 7 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 6 + }, + { + "gototarget": -1, + "patternindex": 7 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 6 + }, + { + "gototarget": -1, + "patternindex": 7 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 6 + }, + { + "gototarget": -1, + "patternindex": 7 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 6 + }, + { + "gototarget": -1, + "patternindex": 7 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 5 + }, + { + "gototarget": -1, + "patternindex": 6 + }, + { + "gototarget": -1, + "patternindex": 7 + }, + { + "gototarget": -1, + "patternindex": 0 + }, + { + "gototarget": -1, + "patternindex": 0 + }, + { + "gototarget": -1, + "patternindex": 2 + }, + { + "gototarget": -1, + "patternindex": 3 + }, + { + "gototarget": -1, + "patternindex": 13 + }, + { + "gototarget": -1, + "patternindex": 13 + }, + { + "gototarget": -1, + "patternindex": 13 + }, + { + "gototarget": 0, + "patternindex": 13 + } + ] + }, + { + "sequence": [ + { + "gototarget": -1, + "patternindex": 4 + }, + { + "gototarget": -1, + "patternindex": 4 + }, + { + "gototarget": -1, + "patternindex": 8 + }, + { + "gototarget": -1, + "patternindex": 8 + }, + { + "gototarget": -1, + "patternindex": 8 + }, + { + "gototarget": -1, + "patternindex": 8 + }, + { + "gototarget": -1, + "patternindex": 4 + }, + { + "gototarget": -1, + "patternindex": 4 + }, + { + "gototarget": -1, + "patternindex": 1 + }, + { + "gototarget": -1, + "patternindex": 1 + }, + { + "gototarget": -1, + "patternindex": 1 + }, + { + "gototarget": -1, + "patternindex": 1 + }, + { + "gototarget": -1, + "patternindex": 9 + }, + { + "gototarget": -1, + "patternindex": 10 + }, + { + "gototarget": -1, + "patternindex": 9 + }, + { + "gototarget": -1, + "patternindex": 10 + }, + { + "gototarget": -1, + "patternindex": 11 + }, + { + "gototarget": -1, + "patternindex": 12 + }, + { + "gototarget": -1, + "patternindex": 11 + }, + { + "gototarget": -1, + "patternindex": 12 + }, + { + "gototarget": -1, + "patternindex": 1 + }, + { + "gototarget": -1, + "patternindex": 1 + }, + { + "gototarget": -1, + "patternindex": 1 + }, + { + "gototarget": 0, + "patternindex": 1 + } + ] + } + ], + "evenspeed": 6, + "globalspeed": true, + "instruments": [ + { + "envelopeLength": 16, + "frequencies": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "name": "bassline", + "releaseStart": 15, + "sustainStart": 14, + "version": 1, + "volumes": [ + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 0, + 0 + ], + "waveform": 6 + }, + { + "envelopeLength": 6, + "frequencies": [ + 0, + 0, + 0, + -1, + 0, + 0 + ], + "name": "Chords", + "releaseStart": 5, + "sustainStart": 2, + "version": 1, + "volumes": [ + 13, + 12, + 11, + 10, + 10, + 6 + ], + "waveform": 16 + }, + { + "envelopeLength": 2, + "frequencies": [ + 0, + 0 + ], + "name": "vide", + "releaseStart": 1, + "sustainStart": 0, + "version": 1, + "volumes": [ + 0, + 0 + ], + "waveform": 16 + }, + { + "envelopeLength": 2, + "frequencies": [ + 0, + 0 + ], + "name": "---", + "releaseStart": 1, + "sustainStart": 0, + "version": 1, + "volumes": [ + 0, + 0 + ], + "waveform": 16 + }, + { + "envelopeLength": 2, + "frequencies": [ + 0, + 0 + ], + "name": "---", + "releaseStart": 1, + "sustainStart": 0, + "version": 1, + "volumes": [ + 0, + 0 + ], + "waveform": 16 + }, + { + "envelopeLength": 2, + "frequencies": [ + 0, + 0 + ], + "name": "---", + "releaseStart": 1, + "sustainStart": 0, + "version": 1, + "volumes": [ + 0, + 0 + ], + "waveform": 16 + }, + { + "envelopeLength": 2, + "frequencies": [ + 0, + 0 + ], + "name": "---", + "releaseStart": 1, + "sustainStart": 0, + "version": 1, + "volumes": [ + 0, + 0 + ], + "waveform": 16 + } + ], + "metaAuthor": "glafouk", + "metaComment": "", + "metaName": "ishtar", + "oddspeed": 6, + "patterns": [ + { + "evenspeed": 3, + "name": "b+d0a", + "notes": [ + { + "number": 0, + "type": 3, + "value": 24 + }, + { + "number": 0, + "type": 1, + "value": 24 + }, + { + "number": 2, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 1, + "value": 24 + }, + { + "number": 0, + "type": 0, + "value": 24 + }, + { + "number": 0, + "type": 3, + "value": 24 + }, + { + "number": 0, + "type": 1, + "value": 24 + }, + { + "number": 2, + "type": 0, + "value": 27 + }, + { + "number": 0, + "type": 0, + "value": 24 + }, + { + "number": 0, + "type": 3, + "value": 0 + }, + { + "number": 2, + "type": 0, + "value": 0 + }, + { + "number": 2, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + } + ], + "oddspeed": 3 + }, + { + "evenspeed": 3, + "name": "mel0a", + "notes": [ + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 1, + "type": 0, + "value": 45 + }, + { + "number": 1, + "type": 1, + "value": 17 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 1, + "type": 0, + "value": 44 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 0, + "value": 31 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 1, + "type": 1, + "value": 26 + } + ], + "oddspeed": 3 + }, + { + "evenspeed": 3, + "name": "b+d0b", + "notes": [ + { + "number": 0, + "type": 3, + "value": 24 + }, + { + "number": 0, + "type": 1, + "value": 20 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 1, + "value": 20 + }, + { + "number": 0, + "type": 0, + "value": 24 + }, + { + "number": 0, + "type": 3, + "value": 24 + }, + { + "number": 0, + "type": 1, + "value": 20 + }, + { + "number": 0, + "type": 0, + "value": 27 + }, + { + "number": 0, + "type": 0, + "value": 24 + }, + { + "number": 0, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + } + ], + "oddspeed": 3 + }, + { + "evenspeed": 3, + "name": "b+d0c", + "notes": [ + { + "number": 0, + "type": 3, + "value": 24 + }, + { + "number": 0, + "type": 1, + "value": 28 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 1, + "value": 28 + }, + { + "number": 0, + "type": 0, + "value": 24 + }, + { + "number": 0, + "type": 3, + "value": 24 + }, + { + "number": 0, + "type": 1, + "value": 28 + }, + { + "number": 0, + "type": 0, + "value": 27 + }, + { + "number": 0, + "type": 0, + "value": 24 + }, + { + "number": 0, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + } + ], + "oddspeed": 3 + }, + { + "evenspeed": 6, + "name": "s0a", + "notes": [ + { + "number": 2, + "type": 1, + "value": 46 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 2, + "type": 0, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + } + ], + "oddspeed": 6 + }, + { + "evenspeed": 3, + "name": "b+d1a", + "notes": [ + { + "number": 0, + "type": 3, + "value": 24 + }, + { + "number": 0, + "type": 1, + "value": 24 + }, + { + "number": 2, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 1, + "value": 24 + }, + { + "number": 0, + "type": 0, + "value": 24 + }, + { + "number": 0, + "type": 3, + "value": 24 + }, + { + "number": 0, + "type": 1, + "value": 24 + }, + { + "number": 2, + "type": 0, + "value": 27 + }, + { + "number": 0, + "type": 0, + "value": 24 + }, + { + "number": 1, + "type": 3, + "value": 0 + }, + { + "number": 1, + "type": 0, + "value": 0 + }, + { + "number": 2, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + } + ], + "oddspeed": 3 + }, + { + "evenspeed": 3, + "name": "b+d1b", + "notes": [ + { + "number": 0, + "type": 3, + "value": 24 + }, + { + "number": 0, + "type": 1, + "value": 20 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 1, + "value": 20 + }, + { + "number": 0, + "type": 0, + "value": 24 + }, + { + "number": 0, + "type": 3, + "value": 24 + }, + { + "number": 0, + "type": 1, + "value": 20 + }, + { + "number": 0, + "type": 0, + "value": 27 + }, + { + "number": 0, + "type": 0, + "value": 24 + }, + { + "number": 1, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + } + ], + "oddspeed": 3 + }, + { + "evenspeed": 3, + "name": "b+d1c", + "notes": [ + { + "number": 0, + "type": 3, + "value": 24 + }, + { + "number": 0, + "type": 1, + "value": 28 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 1, + "value": 28 + }, + { + "number": 0, + "type": 0, + "value": 24 + }, + { + "number": 0, + "type": 3, + "value": 24 + }, + { + "number": 0, + "type": 1, + "value": 28 + }, + { + "number": 0, + "type": 0, + "value": 27 + }, + { + "number": 0, + "type": 0, + "value": 24 + }, + { + "number": 1, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 3, + "value": 0 + } + ], + "oddspeed": 3 + }, + { + "evenspeed": 6, + "name": "s0b", + "notes": [ + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 2, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 2, + "type": 0, + "value": 0 + }, + { + "number": 2, + "type": 0, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + }, + { + "number": 2, + "type": 3, + "value": 0 + } + ], + "oddspeed": 6 + }, + { + "evenspeed": 6, + "name": "mel1a", + "notes": [ + { + "number": 1, + "type": 1, + "value": 47 + }, + { + "number": 1, + "type": 1, + "value": 31 + }, + { + "number": 1, + "type": 1, + "value": 43 + }, + { + "number": 1, + "type": 1, + "value": 31 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 31 + }, + { + "number": 1, + "type": 1, + "value": 43 + }, + { + "number": 1, + "type": 1, + "value": 31 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 31 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 1, + "type": 0, + "value": 17 + }, + { + "number": 1, + "type": 1, + "value": 17 + }, + { + "number": 1, + "type": 0, + "value": 21 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 0, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 0, + "value": 19 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 1, + "type": 0, + "value": 23 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 0, + "type": 0, + "value": 0 + } + ], + "oddspeed": 6 + }, + { + "evenspeed": 6, + "name": "mel1b", + "notes": [ + { + "number": 1, + "type": 1, + "value": 47 + }, + { + "number": 1, + "type": 1, + "value": 31 + }, + { + "number": 1, + "type": 1, + "value": 43 + }, + { + "number": 1, + "type": 1, + "value": 31 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 31 + }, + { + "number": 1, + "type": 1, + "value": 43 + }, + { + "number": 1, + "type": 1, + "value": 31 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 0, + "value": 26 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 15 + }, + { + "number": 1, + "type": 0, + "value": 17 + }, + { + "number": 1, + "type": 1, + "value": 17 + }, + { + "number": 1, + "type": 0, + "value": 19 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 0, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 0, + "value": 19 + }, + { + "number": 1, + "type": 1, + "value": 17 + }, + { + "number": 1, + "type": 0, + "value": 19 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 0, + "type": 0, + "value": 0 + } + ], + "oddspeed": 6 + }, + { + "evenspeed": 6, + "name": "mel2a", + "notes": [ + { + "number": 1, + "type": 1, + "value": 15 + }, + { + "number": 1, + "type": 1, + "value": 17 + }, + { + "number": 1, + "type": 0, + "value": 19 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 1, + "type": 1, + "value": 17 + }, + { + "number": 1, + "type": 1, + "value": 15 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 0, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 1, + "type": 0, + "value": 17 + }, + { + "number": 1, + "type": 1, + "value": 17 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 1, + "type": 1, + "value": 17 + }, + { + "number": 1, + "type": 0, + "value": 19 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 1, + "type": 1, + "value": 15 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 15 + }, + { + "number": 1, + "type": 1, + "value": 13 + }, + { + "number": 1, + "type": 1, + "value": 15 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 17 + }, + { + "number": 0, + "type": 0, + "value": 0 + } + ], + "oddspeed": 6 + }, + { + "evenspeed": 6, + "name": "mel2b", + "notes": [ + { + "number": 1, + "type": 1, + "value": 15 + }, + { + "number": 1, + "type": 1, + "value": 17 + }, + { + "number": 1, + "type": 0, + "value": 19 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 1, + "type": 0, + "value": 15 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 0, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 1, + "type": 0, + "value": 19 + }, + { + "number": 1, + "type": 0, + "value": 19 + }, + { + "number": 1, + "type": 0, + "value": 17 + }, + { + "number": 1, + "type": 1, + "value": 15 + }, + { + "number": 1, + "type": 1, + "value": 17 + }, + { + "number": 1, + "type": 0, + "value": 17 + }, + { + "number": 1, + "type": 1, + "value": 19 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 1, + "type": 0, + "value": 15 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 31 + }, + { + "number": 1, + "type": 1, + "value": 26 + }, + { + "number": 1, + "type": 1, + "value": 31 + }, + { + "number": 1, + "type": 1, + "value": 23 + }, + { + "number": 0, + "type": 0, + "value": 0 + } + ], + "oddspeed": 6 + }, + { + "evenspeed": 3, + "name": "b0", + "notes": [ + { + "number": 0, + "type": 3, + "value": 24 + }, + { + "number": 0, + "type": 0, + "value": 28 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 28 + }, + { + "number": 0, + "type": 0, + "value": 24 + }, + { + "number": 0, + "type": 3, + "value": 24 + }, + { + "number": 0, + "type": 0, + "value": 28 + }, + { + "number": 0, + "type": 0, + "value": 27 + }, + { + "number": 0, + "type": 0, + "value": 24 + }, + { + "number": 0, + "type": 3, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + }, + { + "number": 0, + "type": 0, + "value": 0 + } + ], + "oddspeed": 3 + } + ], + "percussion": [ + { + "envelopeLength": 14, + "frequencies": [ + 1, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13 + ], + "name": "Kick", + "overlay": false, + "version": 1, + "volumes": [ + 15, + 14, + 14, + 11, + 9, + 8, + 8, + 6, + 6, + 6, + 4, + 4, + 2, + 2 + ], + "waveforms": [ + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14, + 14 + ] + }, + { + "envelopeLength": 14, + "frequencies": [ + 4, + 5, + 2, + 10, + 12, + 15, + 18, + 21, + 24, + 25, + 26, + 27, + 30, + 31 + ], + "name": "Snare", + "overlay": false, + "version": 1, + "volumes": [ + 15, + 15, + 15, + 15, + 15, + 15, + 15, + 13, + 10, + 8, + 6, + 5, + 1, + 0 + ], + "waveforms": [ + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8 + ] + }, + { + "envelopeLength": 6, + "frequencies": [ + 1, + 3, + 1, + 5, + 6, + 9 + ], + "name": "Hit", + "overlay": false, + "version": 1, + "volumes": [ + 8, + 7, + 6, + 4, + 2, + 0 + ], + "waveforms": [ + 8, + 8, + 8, + 8, + 8, + 8 + ] + }, + { + "envelopeLength": 1, + "frequencies": [ + 0 + ], + "name": "---", + "overlay": false, + "version": 1, + "volumes": [ + 0 + ], + "waveforms": [ + 8 + ] + }, + { + "envelopeLength": 1, + "frequencies": [ + 0 + ], + "name": "---", + "overlay": false, + "version": 1, + "volumes": [ + 0 + ], + "waveforms": [ + 8 + ] + }, + { + "envelopeLength": 1, + "frequencies": [ + 0 + ], + "name": "---", + "overlay": false, + "version": 1, + "volumes": [ + 0 + ], + "waveforms": [ + 8 + ] + }, + { + "envelopeLength": 1, + "frequencies": [ + 0 + ], + "name": "---", + "overlay": false, + "version": 1, + "volumes": [ + 0 + ], + "waveforms": [ + 8 + ] + }, + { + "envelopeLength": 1, + "frequencies": [ + 0 + ], + "name": "---", + "overlay": false, + "version": 1, + "volumes": [ + 0 + ], + "waveforms": [ + 8 + ] + }, + { + "envelopeLength": 1, + "frequencies": [ + 0 + ], + "name": "---", + "overlay": false, + "version": 1, + "volumes": [ + 0 + ], + "waveforms": [ + 8 + ] + }, + { + "envelopeLength": 1, + "frequencies": [ + 0 + ], + "name": "---", + "overlay": false, + "version": 1, + "volumes": [ + 0 + ], + "waveforms": [ + 8 + ] + }, + { + "envelopeLength": 1, + "frequencies": [ + 0 + ], + "name": "---", + "overlay": false, + "version": 1, + "volumes": [ + 0 + ], + "waveforms": [ + 8 + ] + }, + { + "envelopeLength": 1, + "frequencies": [ + 0 + ], + "name": "---", + "overlay": false, + "version": 1, + "volumes": [ + 0 + ], + "waveforms": [ + 8 + ] + }, + { + "envelopeLength": 1, + "frequencies": [ + 0 + ], + "name": "---", + "overlay": false, + "version": 1, + "volumes": [ + 0 + ], + "waveforms": [ + 8 + ] + }, + { + "envelopeLength": 1, + "frequencies": [ + 0 + ], + "name": "---", + "overlay": false, + "version": 1, + "volumes": [ + 0 + ], + "waveforms": [ + 8 + ] + }, + { + "envelopeLength": 1, + "frequencies": [ + 0 + ], + "name": "---", + "overlay": false, + "version": 1, + "volumes": [ + 0 + ], + "waveforms": [ + 8 + ] + } + ], + "rowsperbeat": 4, + "startpattern0": 0, + "startpattern1": 0, + "tvmode": "pal", + "version": 1 +} diff --git a/vcs.l65 b/vcs.l65 index 528569c..293a7f7 100644 --- a/vcs.l65 +++ b/vcs.l65 @@ -454,3 +454,140 @@ sprite = function(opt) end return sp end + +-- load a TIATracker song and prepare the data for VCS playback +ttt = function(filename) + local f, str = assert(io.open(filename,'r')) str=f:read('*all') f:close() + local s, pos, err = require"dkjson".decode(str) + if err then error(string.format("error parsing JSON file %s: %s", filename, err)) end + + local t = {} + t.version = s.version + t.tvmode = s.tvmode + t.rowsperbeat = s.rowsperbeat + t.author = s.metaAuthor + t.name = s.metaName + t.comment = s.metaComment + t.globalspeed = (s.globalspeed ~= nil and s.globalspeed or true) and 1 or 0 + t.speed = s.evenspeed - 1 + t.oddspeed = s.oddspeed - 1 + local usefunktempo + if t.globalspeed == 1 then usefunktempo = s.evenspeed ~= s.oddspeed + else + for k,v in ipairs(s.channels[1].sequence) do + local pattern = s.patterns[v.patternindex+1] + if pattern.evenspeed ~= pattern.oddspeed then usefunktempo=true break end + end + end + t.usefunktempo = usefunktempo and 1 or 0 + t.usegoto,t.useslide,t.useoverlay,t.startswithnotes = 0,0,0,1 + + local patternmap = {} + local instrumentmap,instrumentcount = {},0 + local percussionmap,percussioncount = {},0 + t.insctrltable, t.insadindexes, t.inssustainindexes, t.insreleaseindexes = {}, {}, {}, {} + t.percfreqtable, t.percctrlvoltable, t.percindexes = {}, {}, {} + local insEnvelopeIndex,percEnvelopeIndex = 0,0 + t.insfreqvoltable = {} + t.patternspeeds = {} + t.patterns,t.patternptr = {},{} + t.sequence = { {}, {} } + local ins = table.insert + for channelindex, channel in ipairs(s.channels) do + local gotooffset = 0 + for sequenceindex,sequence in ipairs(channel.sequence) do + local patternindex = sequence.patternindex + 1 + if not patternmap[patternindex] then + patternmap[patternindex] = #t.patterns + local pattern = { name = s.patterns[patternindex].name } + ins(t.patterns, pattern) + for noteindex,note in ipairs(s.patterns[patternindex].notes) do + if note.type == 0 then -- Instrument + if not instrumentmap[note.number] then + instrumentmap[note.number] = instrumentcount + local instrument = s.instruments[note.number+1] + instrumentcount = instrumentcount + (instrument.waveform == 16 and 2 or 1) + local sz = instrument.envelopeLength + while sz > instrument.releaseStart+1 and instrument.frequencies[sz] == 0 and instrument.volumes[sz] == 0 do sz = sz-1 end + for i=1,sz do + ins(t.insfreqvoltable, instrument.frequencies[i]+8 << 4 | instrument.volumes[i]) + end + ins(t.insfreqvoltable, instrument.releaseStart+1, 0) + ins(t.insfreqvoltable, 0) + for i = 1, instrument.waveform == 16 and 2 or 1 do + ins(t.insadindexes, insEnvelopeIndex) + ins(t.inssustainindexes, insEnvelopeIndex + instrument.sustainStart) + ins(t.insreleaseindexes, insEnvelopeIndex + instrument.releaseStart) + end + insEnvelopeIndex = insEnvelopeIndex + sz + 2 + if instrument.waveform == 16 then -- PURE_COMBINED + ins(t.insctrltable, 4) -- PURE_HIGH + ins(t.insctrltable, 12) -- PURE_LOW + else + ins(t.insctrltable, instrument.waveform) + end + end + local valueIns = instrumentmap[note.number] + 1 + if s.instruments[note.number+1].waveform == 16 and note.value > 31 then valueIns=valueIns+1 end + ins(pattern, valueIns<<5 | note.value&0x1f) + elseif note.type == 1 then -- Percussion + if not percussionmap[note.number] then + percussionmap[note.number] = percussioncount + percussioncount = percussioncount + 1 + local percussion = s.percussion[note.number+1] + local sz = percussion.envelopeLength + while sz > 0 and percussion.waveforms[sz] == 0 and percussion.volumes[sz] == 0 do sz = sz-1 end + for i=1,sz do + local freq = percussion.frequencies[i] + if percussion.overlay and i == sz then freq = freq + 128 end + ins(t.percfreqtable, freq) + ins(t.percctrlvoltable, percussion.waveforms[i] << 4 | percussion.volumes[i]) + end + ins(t.percfreqtable, 0) + ins(t.percctrlvoltable, 0) + ins(t.percindexes, percEnvelopeIndex+1) + percEnvelopeIndex = percEnvelopeIndex + sz + 1 + if percussion.overlay then t.useoverlay = 1 end + end + ins(pattern, percussionmap[note.number] + 17) -- NoteFirstPerc + elseif note.type == 2 then -- Hold + ins(pattern, 8) -- NoteHold + if sequenceindex == 1 and noteindex == 1 then t.startswithnotes = 0 end + elseif note.type == 3 then -- Pause + ins(pattern, 16) -- NotePause + elseif note.type == 4 then -- Slide + ins(pattern, 8 + note.value) -- NoteHold + note.value + t.useslide = 1 + end + end + ins(pattern, 0) + if t.globalspeed == 0 then + ins(t.patternspeeds, usefunktempo and (t.speed << 4 | t.oddspeed) or t.speed) + end + end + ins(t.sequence[channelindex], patternmap[patternindex]) + if sequence.gototarget ~= -1 then + t.usegoto = 1 + local value = 128 + sequence.gototarget + gotooffset + gotooffset = gotooffset + 1 + if channelindex == 1 then value = value + #channel.sequence[1] end + assert(value < 256, "goto target in channel " .. (channelindex-1) .. " is out of range: " .. value) + ins(t.sequence[channelindex], value) + end + end + end + + local i + t.c0init,i = s.startpattern0,0 + while i <= t.c0init do + if t.sequence[1][i+1] > 127 then t.c0init = t.c0init + 1 end + i = i+1 + end + t.c1init,i = s.startpattern1,0 + while i <= t.c1init do + if t.sequence[2][i+1] > 127 then t.c1init = t.c1init + 1 end + i = i+1 + end + + return t +end